Mon Apr 22 19:17:47 UTC 2024 I: starting to build errbot/trixie/armhf on jenkins on '2024-04-22 19:17' Mon Apr 22 19:17:47 UTC 2024 I: The jenkins build log is/was available at https://jenkins.debian.net/userContent/reproducible/debian/build_service/armhf_30/2471/console.log Mon Apr 22 19:17:47 UTC 2024 I: Downloading source for trixie/errbot=6.2.0+ds-2 --2024-04-22 19:17:48-- http://deb.debian.org/debian/pool/main/e/errbot/errbot_6.2.0%2bds-2.dsc Connecting to 46.16.76.132:3128... connected. Proxy request sent, awaiting response... 200 OK Length: 2239 (2.2K) [text/prs.lines.tag] Saving to: ‘errbot_6.2.0+ds-2.dsc’ 0K .. 100% 335M=0s 2024-04-22 19:17:48 (335 MB/s) - ‘errbot_6.2.0+ds-2.dsc’ saved [2239/2239] Mon Apr 22 19:17:48 UTC 2024 I: errbot_6.2.0+ds-2.dsc -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 Format: 3.0 (quilt) Source: errbot Binary: errbot Architecture: all Version: 6.2.0+ds-2 Maintainer: Debian Python Team Uploaders: Birger Schacht Homepage: http://errbot.io Standards-Version: 4.5.1 Vcs-Browser: https://salsa.debian.org/python-team/packages/errbot Vcs-Git: https://salsa.debian.org/python-team/packages/errbot.git Build-Depends: debhelper-compat (= 13), dh-sequence-python3, git-core, localehelper, python3-all, python3-ansi, python3-bottle, python3-colorlog, python3-daemonize, python3-flask, python3-jinja2, python3-markdown, python3-openssl, python3-pygments, python3-pytest, python3-requests, python3-setuptools, python3-webtest, python3-yapsy, python3-dulwich Package-List: errbot deb net optional arch=all Checksums-Sha1: b23eaa6a5b0e436861b433da2aededefa754985c 1341096 errbot_6.2.0+ds.orig.tar.xz 30cc6c3f38cb6e8a1ab84445c8ddca1c54ca40bf 103924 errbot_6.2.0+ds-2.debian.tar.xz Checksums-Sha256: 5a9bd3ce367906518960493f56e2c547047435c02581d4e3cef5c5f6255cb18a 1341096 errbot_6.2.0+ds.orig.tar.xz 0736eefa88da4a42b55b1643b80631c6120a20ff513f0ab78383d9ee6437b50f 103924 errbot_6.2.0+ds-2.debian.tar.xz Files: 8c91bbd9f785227ef8a1747e464fa86e 1341096 errbot_6.2.0+ds.orig.tar.xz 6579125fefef8e820aa69b0cc95ac025 103924 errbot_6.2.0+ds-2.debian.tar.xz -----BEGIN PGP SIGNATURE----- iQJFBAEBCgAvFiEEj23hBDd/OxHnQXSHMfMURUShdBoFAmWmLwYRHHRjaGV0QGRl Ymlhbi5vcmcACgkQMfMURUShdBr3kA//YZNCkpg8bjsp7+uUy01f4wNpI7Ok6SxO AQEkNVGjWlEyjJVkD2iaqTrg2hJt64HlnC8Bb02OL8Ls9CWhqgVKj64iL2bbt1la Ruh1w2gfWEMLy/BlcqwtufRLC6+KHYSHqmrvUOYCOC6wnxCWbmHjD9RxvgHVR9t4 cx2pZXRqArMJE3dO2rVsFvlsQ9n5orcEewZvpMFDuA13LMwdTjGJS3iYPpSdYgTi sTJGJ24Cg3n/t65FE/zwfmZBFdsO4OQQEZK+tQ59YndOMnP0WSLu+etiI24RDUGz HhnPAM2JxbMKRrjqTXV8qbErgCupGA8f8rotiWegiUaDGoJq4oBeIP5gVYkmJKFZ 9P9YN+1jQerPhmUqOOd1l2Q2wKMq+O24Zv12hif3036h0+H/46qUlKI+kwFSt4yM DhBqH4UW39nl9eZ3RAqx2efILCkgjL++NjZXUouYLeU0HY2z4c5sTe5Qdr1lnrcs oeRUNOvPfN+TkQShzHNUm21w/YvVYDxO3GCbWZTcgvFJnIhiSR02QIytKPtNIM7e qzzDgYSLM6+E09r7KPSdqAPq9bOuclYWXqMJKCf6QkeaBNdYKp7d9ZhhHnN/se+r 0tVQitqS8bA/G7smj0frqESTmXgp3NFw37rTfO19UqAiP4pzPkxT3Pbu0SULMbxt XlBZRm+alRI= =xyF4 -----END PGP SIGNATURE----- Mon Apr 22 19:17:48 UTC 2024 I: Checking whether the package is not for us Mon Apr 22 19:17:48 UTC 2024 I: Starting 1st build on remote node ff64a-armhf-rb.debian.net. Mon Apr 22 19:17:48 UTC 2024 I: Preparing to do remote build '1' on ff64a-armhf-rb.debian.net. Mon Apr 22 19:26:40 UTC 2024 I: Deleting $TMPDIR on ff64a-armhf-rb.debian.net. I: pbuilder: network access will be disabled during build I: Current time: Mon Apr 22 07:17:58 -12 2024 I: pbuilder-time-stamp: 1713813478 I: Building the build Environment I: extracting base tarball [/var/cache/pbuilder/trixie-reproducible-base.tgz] I: copying local configuration W: --override-config is not set; not updating apt.conf Read the manpage for details. I: mounting /proc filesystem I: mounting /sys filesystem I: creating /{dev,run}/shm I: mounting /dev/pts filesystem I: redirecting /dev/ptmx to /dev/pts/ptmx I: policy-rc.d already exists I: Copying source file I: copying [errbot_6.2.0+ds-2.dsc] I: copying [./errbot_6.2.0+ds.orig.tar.xz] I: copying [./errbot_6.2.0+ds-2.debian.tar.xz] I: Extracting source gpgv: Signature made Tue Jan 16 07:23:50 2024 gpgv: using RSA key 8F6DE104377F3B11E741748731F3144544A1741A gpgv: issuer "tchet@debian.org" gpgv: Can't check signature: No public key dpkg-source: warning: cannot verify inline signature for ./errbot_6.2.0+ds-2.dsc: no acceptable signature found 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-2.debian.tar.xz dpkg-source: info: using patch list from debian/patches/series dpkg-source: info: applying 0002-Remove-pygments-markdown-lexer-dependency.patch I: Not using root during the build. I: Installing the build-deps I: user script /srv/workspace/pbuilder/3678/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=5 ' DISTRIBUTION='trixie' HOME='/root' HOST_ARCH='armhf' IFS=' ' INVOCATION_ID='e7ca3b97ed154b0bb584bbdf438878d4' 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='3678' 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.pqsrZdXB/pbuilderrc_ueRw --distribution trixie --hookdir /etc/pbuilder/first-build-hooks --debbuildopts -b --basetgz /var/cache/pbuilder/trixie-reproducible-base.tgz --buildresult /srv/reproducible-results/rbuild-debian/r-b-build.pqsrZdXB/b1 --logfile b1/build.log errbot_6.2.0+ds-2.dsc' SUDO_GID='114' SUDO_UID='109' 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 ff64a 6.1.0-20-arm64 #1 SMP Debian 6.1.85-1 (2024-04-11) aarch64 GNU/Linux I: ls -l /bin lrwxrwxrwx 1 root root 7 Apr 21 07:16 /bin -> usr/bin I: user script /srv/workspace/pbuilder/3678/tmp/hooks/D02_print_environment finished -> Attempting to satisfy build-dependencies -> Creating pbuilder-satisfydepends-dummy package Package: pbuilder-satisfydepends-dummy Version: 0.invalid.0 Architecture: armhf Maintainer: Debian Pbuilder Team Description: Dummy package to satisfy dependencies with aptitude - created by pbuilder This package was created automatically by pbuilder to satisfy the build-dependencies of the package being currently built. Depends: debhelper-compat (= 13), dh-sequence-python3, git-core, localehelper, python3-all, python3-ansi, python3-bottle, python3-colorlog, python3-daemonize, python3-flask, python3-jinja2, python3-markdown, python3-openssl, python3-pygments, python3-pytest, python3-requests, python3-setuptools, python3-webtest, python3-yapsy, python3-dulwich dpkg-deb: building package 'pbuilder-satisfydepends-dummy' in '/tmp/satisfydepends-aptitude/pbuilder-satisfydepends-dummy.deb'. Selecting previously unselected package pbuilder-satisfydepends-dummy. (Reading database ... 19635 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} libcurl3-gnutls{a} libdebhelper-perl{a} libelf1{a} liberror-perl{a} libexpat1{a} libfile-stripnondeterminism-perl{a} libicu72{a} libjs-jquery{a} libldap-2.5-0{a} libmagic-mgc{a} libmagic1{a} libnghttp2-14{a} libpipeline1{a} libpsl5{a} libpython3-stdlib{a} libpython3.11-minimal{a} libpython3.11-stdlib{a} libpython3.12-minimal{a} libpython3.12-stdlib{a} libreadline8{a} librtmp1{a} libsasl2-2{a} libsasl2-modules-db{a} libssh2-1{a} libsub-override-perl{a} libtool{a} libuchardet0{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-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-colorama{a} python3-colorlog{a} python3-cryptography{a} python3-daemonize{a} python3-distutils{a} python3-dulwich{a} python3-flask{a} python3-idna{a} python3-iniconfig{a} python3-itsdangerous{a} python3-jinja2{a} python3-lib2to3{a} python3-markdown{a} python3-markupsafe{a} python3-minimal{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-six{a} python3-soupsieve{a} python3-tempita{a} python3-urllib3{a} python3-waitress{a} python3-webob{a} python3-webtest{a} python3-werkzeug{a} python3-yapsy{a} python3.11{a} python3.11-minimal{a} python3.12{a} python3.12-minimal{a} readline-common{a} sensible-utils{a} tzdata{a} The following packages are RECOMMENDED but will NOT be installed: curl javascript-common 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-simplejson python3-yaml wget 0 packages upgraded, 109 newly installed, 0 to remove and 0 not upgraded. Need to get 49.5 MB of archives. After unpacking 195 MB will be used. Writing extended state information... Get: 1 http://deb.debian.org/debian trixie/main armhf libpython3.11-minimal armhf 3.11.8-1 [802 kB] Get: 2 http://deb.debian.org/debian trixie/main armhf libexpat1 armhf 2.6.2-1 [83.5 kB] Get: 3 http://deb.debian.org/debian trixie/main armhf python3.11-minimal armhf 3.11.8-1 [1707 kB] Get: 4 http://deb.debian.org/debian trixie/main armhf python3-minimal armhf 3.11.6-1 [26.2 kB] Get: 5 http://deb.debian.org/debian trixie/main armhf media-types all 10.1.0 [26.9 kB] Get: 6 http://deb.debian.org/debian trixie/main armhf netbase all 6.4 [12.8 kB] Get: 7 http://deb.debian.org/debian trixie/main armhf tzdata all 2024a-1 [255 kB] Get: 8 http://deb.debian.org/debian trixie/main armhf readline-common all 8.2-3 [69.1 kB] Get: 9 http://deb.debian.org/debian trixie/main armhf libreadline8 armhf 8.2-3+b1 [144 kB] Get: 10 http://deb.debian.org/debian trixie/main armhf libpython3.11-stdlib armhf 3.11.8-1 [1709 kB] Get: 11 http://deb.debian.org/debian trixie/main armhf python3.11 armhf 3.11.8-1 [597 kB] Get: 12 http://deb.debian.org/debian trixie/main armhf libpython3-stdlib armhf 3.11.6-1 [9224 B] Get: 13 http://deb.debian.org/debian trixie/main armhf python3 armhf 3.11.6-1 [26.2 kB] Get: 14 http://deb.debian.org/debian trixie/main armhf libpython3.12-minimal armhf 3.12.2-1 [793 kB] Get: 15 http://deb.debian.org/debian trixie/main armhf python3.12-minimal armhf 3.12.2-1 [1845 kB] Get: 16 http://deb.debian.org/debian trixie/main armhf sensible-utils all 0.0.22 [22.4 kB] Get: 17 http://deb.debian.org/debian trixie/main armhf openssl armhf 3.1.5-1 [1208 kB] Get: 18 http://deb.debian.org/debian trixie/main armhf ca-certificates all 20240203 [158 kB] Get: 19 http://deb.debian.org/debian trixie/main armhf libmagic-mgc armhf 1:5.45-2+b1 [314 kB] Get: 20 http://deb.debian.org/debian trixie/main armhf libmagic1 armhf 1:5.45-2+b1 [97.9 kB] Get: 21 http://deb.debian.org/debian trixie/main armhf file armhf 1:5.45-2+b1 [42.2 kB] Get: 22 http://deb.debian.org/debian trixie/main armhf gettext-base armhf 0.21-14+b1 [157 kB] Get: 23 http://deb.debian.org/debian trixie/main armhf libuchardet0 armhf 0.0.8-1+b1 [65.7 kB] Get: 24 http://deb.debian.org/debian trixie/main armhf groff-base armhf 1.23.0-3 [1088 kB] Get: 25 http://deb.debian.org/debian trixie/main armhf locales all 2.37-15 [3908 kB] Get: 26 http://deb.debian.org/debian trixie/main armhf bsdextrautils armhf 2.39.3-6 [81.2 kB] Get: 27 http://deb.debian.org/debian trixie/main armhf libpipeline1 armhf 1.5.7-2 [33.3 kB] Get: 28 http://deb.debian.org/debian trixie/main armhf man-db armhf 2.12.0-3 [1367 kB] Get: 29 http://deb.debian.org/debian trixie/main armhf m4 armhf 1.4.19-4 [264 kB] Get: 30 http://deb.debian.org/debian trixie/main armhf autoconf all 2.71-3 [332 kB] Get: 31 http://deb.debian.org/debian trixie/main armhf autotools-dev all 20220109.1 [51.6 kB] Get: 32 http://deb.debian.org/debian trixie/main armhf automake all 1:1.16.5-1.3 [823 kB] Get: 33 http://deb.debian.org/debian trixie/main armhf autopoint all 0.21-14 [496 kB] Get: 34 http://deb.debian.org/debian trixie/main armhf libdebhelper-perl all 13.15.3 [88.0 kB] Get: 35 http://deb.debian.org/debian trixie/main armhf libtool all 2.4.7-7 [517 kB] Get: 36 http://deb.debian.org/debian trixie/main armhf dh-autoreconf all 20 [17.1 kB] Get: 37 http://deb.debian.org/debian trixie/main armhf libarchive-zip-perl all 1.68-1 [104 kB] Get: 38 http://deb.debian.org/debian trixie/main armhf libsub-override-perl all 0.10-1 [10.6 kB] Get: 39 http://deb.debian.org/debian trixie/main armhf libfile-stripnondeterminism-perl all 1.13.1-1 [19.4 kB] Get: 40 http://deb.debian.org/debian trixie/main armhf dh-strip-nondeterminism all 1.13.1-1 [8620 B] Get: 41 http://deb.debian.org/debian trixie/main armhf libelf1 armhf 0.190-1+b1 [171 kB] Get: 42 http://deb.debian.org/debian trixie/main armhf dwz armhf 0.15-1 [101 kB] Get: 43 http://deb.debian.org/debian trixie/main armhf libicu72 armhf 72.1-4+b1 [9070 kB] Get: 44 http://deb.debian.org/debian trixie/main armhf libxml2 armhf 2.9.14+dfsg-1.3+b2 [599 kB] Get: 45 http://deb.debian.org/debian trixie/main armhf gettext armhf 0.21-14+b1 [1230 kB] Get: 46 http://deb.debian.org/debian trixie/main armhf intltool-debian all 0.35.0+20060710.6 [22.9 kB] Get: 47 http://deb.debian.org/debian trixie/main armhf po-debconf all 1.0.21+nmu1 [248 kB] Get: 48 http://deb.debian.org/debian trixie/main armhf debhelper all 13.15.3 [901 kB] Get: 49 http://deb.debian.org/debian trixie/main armhf python3-pkg-resources all 68.1.2-2 [241 kB] Get: 50 http://deb.debian.org/debian trixie/main armhf python3-lib2to3 all 3.11.5-1 [77.5 kB] Get: 51 http://deb.debian.org/debian trixie/main armhf python3-distutils all 3.11.5-1 [131 kB] Get: 52 http://deb.debian.org/debian trixie/main armhf python3-setuptools all 68.1.2-2 [468 kB] Get: 53 http://deb.debian.org/debian trixie/main armhf dh-python all 6.20240401 [107 kB] Get: 54 http://deb.debian.org/debian trixie/main armhf libbrotli1 armhf 1.1.0-2+b3 [284 kB] Get: 55 http://deb.debian.org/debian trixie/main armhf libsasl2-modules-db armhf 2.1.28+dfsg1-4+b1 [18.2 kB] Get: 56 http://deb.debian.org/debian trixie/main armhf libsasl2-2 armhf 2.1.28+dfsg1-4+b1 [50.1 kB] Get: 57 http://deb.debian.org/debian trixie/main armhf libldap-2.5-0 armhf 2.5.13+dfsg-5+b3 [158 kB] Get: 58 http://deb.debian.org/debian trixie/main armhf libnghttp2-14 armhf 1.59.0-1 [62.0 kB] Get: 59 http://deb.debian.org/debian trixie/main armhf libpsl5 armhf 0.21.2-1+b1 [57.8 kB] Get: 60 http://deb.debian.org/debian trixie/main armhf librtmp1 armhf 2.4+20151223.gitfa8646d.1-2+b2 [55.2 kB] Get: 61 http://deb.debian.org/debian trixie/main armhf libssh2-1 armhf 1.11.0-4 [197 kB] Get: 62 http://deb.debian.org/debian trixie/main armhf libcurl3-gnutls armhf 8.5.0-2 [378 kB] Get: 63 http://deb.debian.org/debian trixie/main armhf liberror-perl all 0.17029-2 [29.0 kB] Get: 64 http://deb.debian.org/debian trixie/main armhf git-man all 1:2.43.0-1 [2112 kB] Get: 65 http://deb.debian.org/debian trixie/main armhf git armhf 1:2.43.0-1 [6291 kB] Get: 66 http://deb.debian.org/debian trixie/main armhf libjs-jquery all 3.6.1+dfsg+~3.5.14-1 [326 kB] Get: 67 http://deb.debian.org/debian trixie/main armhf libpython3.12-stdlib armhf 3.12.2-1 [1800 kB] Get: 68 http://deb.debian.org/debian trixie/main armhf localehelper all 0.1.4-3.1 [7928 B] Get: 69 http://deb.debian.org/debian trixie/main armhf python3.12 armhf 3.12.2-1 [653 kB] Get: 70 http://deb.debian.org/debian trixie/main armhf python3-all armhf 3.11.6-1 [1060 B] Get: 71 http://deb.debian.org/debian trixie/main armhf python3-ansi all 0.1.5-2 [6232 B] Get: 72 http://deb.debian.org/debian trixie/main armhf python3-blinker all 1.7.0-1 [15.5 kB] Get: 73 http://deb.debian.org/debian trixie/main armhf python3-bottle all 0.12.25-1 [45.4 kB] Get: 74 http://deb.debian.org/debian trixie/main armhf python3-soupsieve all 2.5-1 [38.1 kB] Get: 75 http://deb.debian.org/debian trixie/main armhf python3-bs4 all 4.12.3-1 [133 kB] Get: 76 http://deb.debian.org/debian trixie/main armhf python3-certifi all 2023.11.17-1 [155 kB] Get: 77 http://deb.debian.org/debian trixie/main armhf python3-cffi-backend armhf 1.16.0-2+b1 [98.9 kB] Get: 78 http://deb.debian.org/debian trixie/main armhf python3-chardet all 5.2.0+dfsg-1 [107 kB] Get: 79 http://deb.debian.org/debian trixie/main armhf python3-charset-normalizer all 3.3.2-1 [51.6 kB] Get: 80 http://deb.debian.org/debian trixie/main armhf python3-colorama all 0.4.6-4 [36.2 kB] Get: 81 http://deb.debian.org/debian trixie/main armhf python3-click all 8.1.7-1 [94.0 kB] Get: 82 http://deb.debian.org/debian trixie/main armhf python3-colorlog all 6.8.2-1 [27.2 kB] Get: 83 http://deb.debian.org/debian trixie/main armhf python3-cryptography armhf 41.0.7-4 [674 kB] Get: 84 http://deb.debian.org/debian trixie/main armhf python3-daemonize all 2.5.0-2 [6300 B] Get: 85 http://deb.debian.org/debian trixie/main armhf python3-six all 1.16.0-6 [16.3 kB] Get: 86 http://deb.debian.org/debian trixie/main armhf python3-urllib3 all 1.26.18-2 [116 kB] Get: 87 http://deb.debian.org/debian trixie/main armhf python3-dulwich armhf 0.21.6-1+b1 [326 kB] Get: 88 http://deb.debian.org/debian trixie/main armhf python3-itsdangerous all 2.1.2-4 [17.1 kB] Get: 89 http://deb.debian.org/debian trixie/main armhf python3-markupsafe armhf 2.1.5-1 [13.9 kB] Get: 90 http://deb.debian.org/debian trixie/main armhf python3-jinja2 all 3.1.3-1 [119 kB] Get: 91 http://deb.debian.org/debian trixie/main armhf python3-werkzeug all 3.0.2-1 [206 kB] Get: 92 http://deb.debian.org/debian trixie/main armhf python3-flask all 3.0.3-1 [104 kB] Get: 93 http://deb.debian.org/debian trixie/main armhf python3-idna all 3.6-2 [37.0 kB] Get: 94 http://deb.debian.org/debian trixie/main armhf python3-iniconfig all 1.1.1-2 [6396 B] Get: 95 http://deb.debian.org/debian trixie/main armhf python3-markdown all 3.6-1 [84.0 kB] Get: 96 http://deb.debian.org/debian trixie/main armhf python3-openssl all 24.1.0-1 [51.9 kB] Get: 97 http://deb.debian.org/debian trixie/main armhf python3-packaging all 24.0-1 [45.5 kB] Get: 98 http://deb.debian.org/debian trixie/main armhf python3-tempita all 0.5.2-7 [15.0 kB] Get: 99 http://deb.debian.org/debian trixie/main armhf python3-paste all 3.9.0-1 [214 kB] Get: 100 http://deb.debian.org/debian trixie/main armhf python3-pastedeploy-tpl all 3.1-1 [8268 B] Get: 101 http://deb.debian.org/debian trixie/main armhf python3-pastedeploy all 3.1-1 [18.3 kB] Get: 102 http://deb.debian.org/debian trixie/main armhf python3-pluggy all 1.4.0-1 [26.2 kB] Get: 103 http://deb.debian.org/debian trixie/main armhf python3-pygments all 2.17.2+dfsg-1 [818 kB] Get: 104 http://deb.debian.org/debian trixie/main armhf python3-pytest all 7.4.4-3 [239 kB] Get: 105 http://deb.debian.org/debian trixie/main armhf python3-requests all 2.31.0+dfsg-1 [68.6 kB] Get: 106 http://deb.debian.org/debian trixie/main armhf python3-waitress all 2.1.2-2 [48.9 kB] Get: 107 http://deb.debian.org/debian trixie/main armhf python3-webob all 1:1.8.7-1 [88.2 kB] Get: 108 http://deb.debian.org/debian trixie/main armhf python3-webtest all 3.0.0-3 [34.6 kB] Get: 109 http://deb.debian.org/debian trixie/main armhf python3-yapsy all 1.12.2-2 [30.4 kB] Fetched 49.5 MB in 2s (22.8 MB/s) debconf: delaying package configuration, since apt-utils is not installed Selecting previously unselected package libpython3.11-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 ... 19635 files and directories currently installed.) Preparing to unpack .../libpython3.11-minimal_3.11.8-1_armhf.deb ... Unpacking libpython3.11-minimal:armhf (3.11.8-1) ... Selecting previously unselected package libexpat1:armhf. Preparing to unpack .../libexpat1_2.6.2-1_armhf.deb ... Unpacking libexpat1:armhf (2.6.2-1) ... Selecting previously unselected package python3.11-minimal. Preparing to unpack .../python3.11-minimal_3.11.8-1_armhf.deb ... Unpacking python3.11-minimal (3.11.8-1) ... Setting up libpython3.11-minimal:armhf (3.11.8-1) ... Setting up libexpat1:armhf (2.6.2-1) ... Setting up python3.11-minimal (3.11.8-1) ... 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 ... 19951 files and directories currently installed.) Preparing to unpack .../0-python3-minimal_3.11.6-1_armhf.deb ... Unpacking python3-minimal (3.11.6-1) ... 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_2024a-1_all.deb ... Unpacking tzdata (2024a-1) ... Selecting previously unselected package readline-common. Preparing to unpack .../4-readline-common_8.2-3_all.deb ... Unpacking readline-common (8.2-3) ... Selecting previously unselected package libreadline8:armhf. Preparing to unpack .../5-libreadline8_8.2-3+b1_armhf.deb ... Unpacking libreadline8:armhf (8.2-3+b1) ... Selecting previously unselected package libpython3.11-stdlib:armhf. Preparing to unpack .../6-libpython3.11-stdlib_3.11.8-1_armhf.deb ... Unpacking libpython3.11-stdlib:armhf (3.11.8-1) ... Selecting previously unselected package python3.11. Preparing to unpack .../7-python3.11_3.11.8-1_armhf.deb ... Unpacking python3.11 (3.11.8-1) ... Selecting previously unselected package libpython3-stdlib:armhf. Preparing to unpack .../8-libpython3-stdlib_3.11.6-1_armhf.deb ... Unpacking libpython3-stdlib:armhf (3.11.6-1) ... Setting up python3-minimal (3.11.6-1) ... 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 ... 20939 files and directories currently installed.) Preparing to unpack .../00-python3_3.11.6-1_armhf.deb ... Unpacking python3 (3.11.6-1) ... Selecting previously unselected package libpython3.12-minimal:armhf. Preparing to unpack .../01-libpython3.12-minimal_3.12.2-1_armhf.deb ... Unpacking libpython3.12-minimal:armhf (3.12.2-1) ... Selecting previously unselected package python3.12-minimal. Preparing to unpack .../02-python3.12-minimal_3.12.2-1_armhf.deb ... Unpacking python3.12-minimal (3.12.2-1) ... Selecting previously unselected package sensible-utils. Preparing to unpack .../03-sensible-utils_0.0.22_all.deb ... Unpacking sensible-utils (0.0.22) ... Selecting previously unselected package openssl. Preparing to unpack .../04-openssl_3.1.5-1_armhf.deb ... Unpacking openssl (3.1.5-1) ... Selecting previously unselected package ca-certificates. Preparing to unpack .../05-ca-certificates_20240203_all.deb ... Unpacking ca-certificates (20240203) ... Selecting previously unselected package libmagic-mgc. Preparing to unpack .../06-libmagic-mgc_1%3a5.45-2+b1_armhf.deb ... Unpacking libmagic-mgc (1:5.45-2+b1) ... Selecting previously unselected package libmagic1:armhf. Preparing to unpack .../07-libmagic1_1%3a5.45-2+b1_armhf.deb ... Unpacking libmagic1:armhf (1:5.45-2+b1) ... Selecting previously unselected package file. Preparing to unpack .../08-file_1%3a5.45-2+b1_armhf.deb ... Unpacking file (1:5.45-2+b1) ... Selecting previously unselected package gettext-base. Preparing to unpack .../09-gettext-base_0.21-14+b1_armhf.deb ... Unpacking gettext-base (0.21-14+b1) ... Selecting previously unselected package libuchardet0:armhf. Preparing to unpack .../10-libuchardet0_0.0.8-1+b1_armhf.deb ... Unpacking libuchardet0:armhf (0.0.8-1+b1) ... Selecting previously unselected package groff-base. Preparing to unpack .../11-groff-base_1.23.0-3_armhf.deb ... Unpacking groff-base (1.23.0-3) ... Selecting previously unselected package locales. Preparing to unpack .../12-locales_2.37-15_all.deb ... Unpacking locales (2.37-15) ... Selecting previously unselected package bsdextrautils. Preparing to unpack .../13-bsdextrautils_2.39.3-6_armhf.deb ... Unpacking bsdextrautils (2.39.3-6) ... Selecting previously unselected package libpipeline1:armhf. Preparing to unpack .../14-libpipeline1_1.5.7-2_armhf.deb ... Unpacking libpipeline1:armhf (1.5.7-2) ... Selecting previously unselected package man-db. Preparing to unpack .../15-man-db_2.12.0-3_armhf.deb ... Unpacking man-db (2.12.0-3) ... Selecting previously unselected package m4. Preparing to unpack .../16-m4_1.4.19-4_armhf.deb ... Unpacking m4 (1.4.19-4) ... Selecting previously unselected package autoconf. Preparing to unpack .../17-autoconf_2.71-3_all.deb ... Unpacking autoconf (2.71-3) ... Selecting previously unselected package autotools-dev. Preparing to unpack .../18-autotools-dev_20220109.1_all.deb ... Unpacking autotools-dev (20220109.1) ... Selecting previously unselected package automake. Preparing to unpack .../19-automake_1%3a1.16.5-1.3_all.deb ... Unpacking automake (1:1.16.5-1.3) ... Selecting previously unselected package autopoint. Preparing to unpack .../20-autopoint_0.21-14_all.deb ... Unpacking autopoint (0.21-14) ... Selecting previously unselected package libdebhelper-perl. Preparing to unpack .../21-libdebhelper-perl_13.15.3_all.deb ... Unpacking libdebhelper-perl (13.15.3) ... Selecting previously unselected package libtool. Preparing to unpack .../22-libtool_2.4.7-7_all.deb ... Unpacking libtool (2.4.7-7) ... Selecting previously unselected package dh-autoreconf. Preparing to unpack .../23-dh-autoreconf_20_all.deb ... Unpacking dh-autoreconf (20) ... Selecting previously unselected package libarchive-zip-perl. Preparing to unpack .../24-libarchive-zip-perl_1.68-1_all.deb ... Unpacking libarchive-zip-perl (1.68-1) ... Selecting previously unselected package libsub-override-perl. Preparing to unpack .../25-libsub-override-perl_0.10-1_all.deb ... Unpacking libsub-override-perl (0.10-1) ... Selecting previously unselected package libfile-stripnondeterminism-perl. Preparing to unpack .../26-libfile-stripnondeterminism-perl_1.13.1-1_all.deb ... Unpacking libfile-stripnondeterminism-perl (1.13.1-1) ... Selecting previously unselected package dh-strip-nondeterminism. Preparing to unpack .../27-dh-strip-nondeterminism_1.13.1-1_all.deb ... Unpacking dh-strip-nondeterminism (1.13.1-1) ... Selecting previously unselected package libelf1:armhf. Preparing to unpack .../28-libelf1_0.190-1+b1_armhf.deb ... Unpacking libelf1:armhf (0.190-1+b1) ... Selecting previously unselected package dwz. Preparing to unpack .../29-dwz_0.15-1_armhf.deb ... Unpacking dwz (0.15-1) ... Selecting previously unselected package libicu72:armhf. Preparing to unpack .../30-libicu72_72.1-4+b1_armhf.deb ... Unpacking libicu72:armhf (72.1-4+b1) ... Selecting previously unselected package libxml2:armhf. Preparing to unpack .../31-libxml2_2.9.14+dfsg-1.3+b2_armhf.deb ... Unpacking libxml2:armhf (2.9.14+dfsg-1.3+b2) ... Selecting previously unselected package gettext. Preparing to unpack .../32-gettext_0.21-14+b1_armhf.deb ... Unpacking gettext (0.21-14+b1) ... Selecting previously unselected package intltool-debian. Preparing to unpack .../33-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 .../34-po-debconf_1.0.21+nmu1_all.deb ... Unpacking po-debconf (1.0.21+nmu1) ... Selecting previously unselected package debhelper. Preparing to unpack .../35-debhelper_13.15.3_all.deb ... Unpacking debhelper (13.15.3) ... Selecting previously unselected package python3-pkg-resources. Preparing to unpack .../36-python3-pkg-resources_68.1.2-2_all.deb ... Unpacking python3-pkg-resources (68.1.2-2) ... Selecting previously unselected package python3-lib2to3. Preparing to unpack .../37-python3-lib2to3_3.11.5-1_all.deb ... Unpacking python3-lib2to3 (3.11.5-1) ... Selecting previously unselected package python3-distutils. Preparing to unpack .../38-python3-distutils_3.11.5-1_all.deb ... Unpacking python3-distutils (3.11.5-1) ... Selecting previously unselected package python3-setuptools. Preparing to unpack .../39-python3-setuptools_68.1.2-2_all.deb ... Unpacking python3-setuptools (68.1.2-2) ... Selecting previously unselected package dh-python. Preparing to unpack .../40-dh-python_6.20240401_all.deb ... Unpacking dh-python (6.20240401) ... Selecting previously unselected package libbrotli1:armhf. Preparing to unpack .../41-libbrotli1_1.1.0-2+b3_armhf.deb ... Unpacking libbrotli1:armhf (1.1.0-2+b3) ... Selecting previously unselected package libsasl2-modules-db:armhf. Preparing to unpack .../42-libsasl2-modules-db_2.1.28+dfsg1-4+b1_armhf.deb ... Unpacking libsasl2-modules-db:armhf (2.1.28+dfsg1-4+b1) ... Selecting previously unselected package libsasl2-2:armhf. Preparing to unpack .../43-libsasl2-2_2.1.28+dfsg1-4+b1_armhf.deb ... Unpacking libsasl2-2:armhf (2.1.28+dfsg1-4+b1) ... Selecting previously unselected package libldap-2.5-0:armhf. Preparing to unpack .../44-libldap-2.5-0_2.5.13+dfsg-5+b3_armhf.deb ... Unpacking libldap-2.5-0:armhf (2.5.13+dfsg-5+b3) ... Selecting previously unselected package libnghttp2-14:armhf. Preparing to unpack .../45-libnghttp2-14_1.59.0-1_armhf.deb ... Unpacking libnghttp2-14:armhf (1.59.0-1) ... Selecting previously unselected package libpsl5:armhf. Preparing to unpack .../46-libpsl5_0.21.2-1+b1_armhf.deb ... Unpacking libpsl5:armhf (0.21.2-1+b1) ... Selecting previously unselected package librtmp1:armhf. Preparing to unpack .../47-librtmp1_2.4+20151223.gitfa8646d.1-2+b2_armhf.deb ... Unpacking librtmp1:armhf (2.4+20151223.gitfa8646d.1-2+b2) ... Selecting previously unselected package libssh2-1:armhf. Preparing to unpack .../48-libssh2-1_1.11.0-4_armhf.deb ... Unpacking libssh2-1:armhf (1.11.0-4) ... Selecting previously unselected package libcurl3-gnutls:armhf. Preparing to unpack .../49-libcurl3-gnutls_8.5.0-2_armhf.deb ... Unpacking libcurl3-gnutls:armhf (8.5.0-2) ... Selecting previously unselected package liberror-perl. Preparing to unpack .../50-liberror-perl_0.17029-2_all.deb ... Unpacking liberror-perl (0.17029-2) ... Selecting previously unselected package git-man. Preparing to unpack .../51-git-man_1%3a2.43.0-1_all.deb ... Unpacking git-man (1:2.43.0-1) ... Selecting previously unselected package git. Preparing to unpack .../52-git_1%3a2.43.0-1_armhf.deb ... Unpacking git (1:2.43.0-1) ... Selecting previously unselected package libjs-jquery. Preparing to unpack .../53-libjs-jquery_3.6.1+dfsg+~3.5.14-1_all.deb ... Unpacking libjs-jquery (3.6.1+dfsg+~3.5.14-1) ... Selecting previously unselected package libpython3.12-stdlib:armhf. Preparing to unpack .../54-libpython3.12-stdlib_3.12.2-1_armhf.deb ... Unpacking libpython3.12-stdlib:armhf (3.12.2-1) ... Selecting previously unselected package localehelper. Preparing to unpack .../55-localehelper_0.1.4-3.1_all.deb ... Unpacking localehelper (0.1.4-3.1) ... Selecting previously unselected package python3.12. Preparing to unpack .../56-python3.12_3.12.2-1_armhf.deb ... Unpacking python3.12 (3.12.2-1) ... Selecting previously unselected package python3-all. Preparing to unpack .../57-python3-all_3.11.6-1_armhf.deb ... Unpacking python3-all (3.11.6-1) ... Selecting previously unselected package python3-ansi. Preparing to unpack .../58-python3-ansi_0.1.5-2_all.deb ... Unpacking python3-ansi (0.1.5-2) ... Selecting previously unselected package python3-blinker. Preparing to unpack .../59-python3-blinker_1.7.0-1_all.deb ... Unpacking python3-blinker (1.7.0-1) ... Selecting previously unselected package python3-bottle. Preparing to unpack .../60-python3-bottle_0.12.25-1_all.deb ... Unpacking python3-bottle (0.12.25-1) ... Selecting previously unselected package python3-soupsieve. Preparing to unpack .../61-python3-soupsieve_2.5-1_all.deb ... Unpacking python3-soupsieve (2.5-1) ... Selecting previously unselected package python3-bs4. Preparing to unpack .../62-python3-bs4_4.12.3-1_all.deb ... Unpacking python3-bs4 (4.12.3-1) ... Selecting previously unselected package python3-certifi. Preparing to unpack .../63-python3-certifi_2023.11.17-1_all.deb ... Unpacking python3-certifi (2023.11.17-1) ... Selecting previously unselected package python3-cffi-backend:armhf. Preparing to unpack .../64-python3-cffi-backend_1.16.0-2+b1_armhf.deb ... Unpacking python3-cffi-backend:armhf (1.16.0-2+b1) ... Selecting previously unselected package python3-chardet. Preparing to unpack .../65-python3-chardet_5.2.0+dfsg-1_all.deb ... Unpacking python3-chardet (5.2.0+dfsg-1) ... Selecting previously unselected package python3-charset-normalizer. Preparing to unpack .../66-python3-charset-normalizer_3.3.2-1_all.deb ... Unpacking python3-charset-normalizer (3.3.2-1) ... Selecting previously unselected package python3-colorama. Preparing to unpack .../67-python3-colorama_0.4.6-4_all.deb ... Unpacking python3-colorama (0.4.6-4) ... Selecting previously unselected package python3-click. Preparing to unpack .../68-python3-click_8.1.7-1_all.deb ... Unpacking python3-click (8.1.7-1) ... Selecting previously unselected package python3-colorlog. Preparing to unpack .../69-python3-colorlog_6.8.2-1_all.deb ... Unpacking python3-colorlog (6.8.2-1) ... Selecting previously unselected package python3-cryptography. Preparing to unpack .../70-python3-cryptography_41.0.7-4_armhf.deb ... Unpacking python3-cryptography (41.0.7-4) ... Selecting previously unselected package python3-daemonize. Preparing to unpack .../71-python3-daemonize_2.5.0-2_all.deb ... Unpacking python3-daemonize (2.5.0-2) ... Selecting previously unselected package python3-six. Preparing to unpack .../72-python3-six_1.16.0-6_all.deb ... Unpacking python3-six (1.16.0-6) ... Selecting previously unselected package python3-urllib3. Preparing to unpack .../73-python3-urllib3_1.26.18-2_all.deb ... Unpacking python3-urllib3 (1.26.18-2) ... Selecting previously unselected package python3-dulwich. Preparing to unpack .../74-python3-dulwich_0.21.6-1+b1_armhf.deb ... Unpacking python3-dulwich (0.21.6-1+b1) ... Selecting previously unselected package python3-itsdangerous. Preparing to unpack .../75-python3-itsdangerous_2.1.2-4_all.deb ... Unpacking python3-itsdangerous (2.1.2-4) ... Selecting previously unselected package python3-markupsafe. Preparing to unpack .../76-python3-markupsafe_2.1.5-1_armhf.deb ... Unpacking python3-markupsafe (2.1.5-1) ... Selecting previously unselected package python3-jinja2. Preparing to unpack .../77-python3-jinja2_3.1.3-1_all.deb ... Unpacking python3-jinja2 (3.1.3-1) ... Selecting previously unselected package python3-werkzeug. Preparing to unpack .../78-python3-werkzeug_3.0.2-1_all.deb ... Unpacking python3-werkzeug (3.0.2-1) ... Selecting previously unselected package python3-flask. Preparing to unpack .../79-python3-flask_3.0.3-1_all.deb ... Unpacking python3-flask (3.0.3-1) ... Selecting previously unselected package python3-idna. Preparing to unpack .../80-python3-idna_3.6-2_all.deb ... Unpacking python3-idna (3.6-2) ... Selecting previously unselected package python3-iniconfig. Preparing to unpack .../81-python3-iniconfig_1.1.1-2_all.deb ... Unpacking python3-iniconfig (1.1.1-2) ... Selecting previously unselected package python3-markdown. Preparing to unpack .../82-python3-markdown_3.6-1_all.deb ... Unpacking python3-markdown (3.6-1) ... Selecting previously unselected package python3-openssl. Preparing to unpack .../83-python3-openssl_24.1.0-1_all.deb ... Unpacking python3-openssl (24.1.0-1) ... Selecting previously unselected package python3-packaging. Preparing to unpack .../84-python3-packaging_24.0-1_all.deb ... Unpacking python3-packaging (24.0-1) ... Selecting previously unselected package python3-tempita. Preparing to unpack .../85-python3-tempita_0.5.2-7_all.deb ... Unpacking python3-tempita (0.5.2-7) ... Selecting previously unselected package python3-paste. Preparing to unpack .../86-python3-paste_3.9.0-1_all.deb ... Unpacking python3-paste (3.9.0-1) ... Selecting previously unselected package python3-pastedeploy-tpl. Preparing to unpack .../87-python3-pastedeploy-tpl_3.1-1_all.deb ... Unpacking python3-pastedeploy-tpl (3.1-1) ... Selecting previously unselected package python3-pastedeploy. Preparing to unpack .../88-python3-pastedeploy_3.1-1_all.deb ... Unpacking python3-pastedeploy (3.1-1) ... Selecting previously unselected package python3-pluggy. Preparing to unpack .../89-python3-pluggy_1.4.0-1_all.deb ... Unpacking python3-pluggy (1.4.0-1) ... Selecting previously unselected package python3-pygments. Preparing to unpack .../90-python3-pygments_2.17.2+dfsg-1_all.deb ... Unpacking python3-pygments (2.17.2+dfsg-1) ... Selecting previously unselected package python3-pytest. Preparing to unpack .../91-python3-pytest_7.4.4-3_all.deb ... Unpacking python3-pytest (7.4.4-3) ... Selecting previously unselected package python3-requests. Preparing to unpack .../92-python3-requests_2.31.0+dfsg-1_all.deb ... Unpacking python3-requests (2.31.0+dfsg-1) ... Selecting previously unselected package python3-waitress. Preparing to unpack .../93-python3-waitress_2.1.2-2_all.deb ... Unpacking python3-waitress (2.1.2-2) ... Selecting previously unselected package python3-webob. Preparing to unpack .../94-python3-webob_1%3a1.8.7-1_all.deb ... Unpacking python3-webob (1:1.8.7-1) ... Selecting previously unselected package python3-webtest. Preparing to unpack .../95-python3-webtest_3.0.0-3_all.deb ... Unpacking python3-webtest (3.0.0-3) ... Selecting previously unselected package python3-yapsy. Preparing to unpack .../96-python3-yapsy_1.12.2-2_all.deb ... Unpacking python3-yapsy (1.12.2-2) ... Setting up media-types (10.1.0) ... Setting up libpipeline1:armhf (1.5.7-2) ... Setting up libpsl5:armhf (0.21.2-1+b1) ... Setting up libicu72:armhf (72.1-4+b1) ... Setting up bsdextrautils (2.39.3-6) ... Setting up libmagic-mgc (1:5.45-2+b1) ... Setting up libarchive-zip-perl (1.68-1) ... Setting up libdebhelper-perl (13.15.3) ... Setting up libbrotli1:armhf (1.1.0-2+b3) ... Setting up libpython3.12-minimal:armhf (3.12.2-1) ... Setting up libnghttp2-14:armhf (1.59.0-1) ... Setting up libmagic1:armhf (1:5.45-2+b1) ... Setting up gettext-base (0.21-14+b1) ... Setting up m4 (1.4.19-4) ... Setting up file (1:5.45-2+b1) ... Setting up locales (2.37-15) ... locales-all installed, skipping locales generation Setting up libsasl2-modules-db:armhf (2.1.28+dfsg1-4+b1) ... Setting up tzdata (2024a-1) ... Current default time zone: 'Etc/UTC' Local time is now: Mon Apr 22 19:20:07 UTC 2024. Universal Time is now: Mon Apr 22 19:20:07 UTC 2024. 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 librtmp1:armhf (2.4+20151223.gitfa8646d.1-2+b2) ... Setting up python3-pastedeploy-tpl (3.1-1) ... Setting up autopoint (0.21-14) ... Setting up libsasl2-2:armhf (2.1.28+dfsg1-4+b1) ... Setting up autoconf (2.71-3) ... Setting up sensible-utils (0.0.22) ... Setting up libuchardet0:armhf (0.0.8-1+b1) ... Setting up libsub-override-perl (0.10-1) ... Setting up git-man (1:2.43.0-1) ... Setting up libssh2-1:armhf (1.11.0-4) ... Setting up netbase (6.4) ... Setting up libjs-jquery (3.6.1+dfsg+~3.5.14-1) ... Setting up openssl (3.1.5-1) ... Setting up libelf1:armhf (0.190-1+b1) ... Setting up readline-common (8.2-3) ... Setting up libxml2:armhf (2.9.14+dfsg-1.3+b2) ... Setting up automake (1:1.16.5-1.3) ... update-alternatives: using /usr/bin/automake-1.16 to provide /usr/bin/automake (automake) in auto mode Setting up libfile-stripnondeterminism-perl (1.13.1-1) ... Setting up python3.12-minimal (3.12.2-1) ... Setting up gettext (0.21-14+b1) ... Setting up libtool (2.4.7-7) ... Setting up libreadline8:armhf (8.2-3+b1) ... Setting up libldap-2.5-0:armhf (2.5.13+dfsg-5+b3) ... Setting up intltool-debian (0.35.0+20060710.6) ... Setting up dh-autoreconf (20) ... Setting up ca-certificates (20240203) ... Updating certificates in /etc/ssl/certs... 146 added, 0 removed; done. Setting up dh-strip-nondeterminism (1.13.1-1) ... Setting up dwz (0.15-1) ... Setting up groff-base (1.23.0-3) ... Setting up libpython3.12-stdlib:armhf (3.12.2-1) ... Setting up po-debconf (1.0.21+nmu1) ... Setting up libpython3.11-stdlib:armhf (3.11.8-1) ... Setting up python3.12 (3.12.2-1) ... Setting up libcurl3-gnutls:armhf (8.5.0-2) ... Setting up man-db (2.12.0-3) ... Not building database; man-db/auto-update is not 'true'. Setting up git (1:2.43.0-1) ... Setting up libpython3-stdlib:armhf (3.11.6-1) ... Setting up python3.11 (3.11.8-1) ... Setting up debhelper (13.15.3) ... Setting up python3 (3.11.6-1) ... Setting up python3-markupsafe (2.1.5-1) ... Setting up python3-six (1.16.0-6) ... Setting up python3-jinja2 (3.1.3-1) ... Setting up python3-tempita (0.5.2-7) ... Setting up python3-packaging (24.0-1) ... Setting up python3-ansi (0.1.5-2) ... Setting up python3-certifi (2023.11.17-1) ... Setting up python3-werkzeug (3.0.2-1) ... Setting up python3-idna (3.6-2) ... Setting up python3-markdown (3.6-1) ... Setting up python3-urllib3 (1.26.18-2) ... Setting up python3-pluggy (1.4.0-1) ... Setting up python3-dulwich (0.21.6-1+b1) ... Setting up python3-lib2to3 (3.11.5-1) ... Setting up python3-soupsieve (2.5-1) ... Setting up python3-cffi-backend:armhf (1.16.0-2+b1) ... Setting up python3-webob (1:1.8.7-1) ... Setting up python3-yapsy (1.12.2-2) ... /usr/lib/python3/dist-packages/yapsy/__init__.py:73: SyntaxWarning: invalid escape sequence '\W' RE_NON_ALPHANUM = re.compile("\W") /usr/lib/python3/dist-packages/yapsy/__init___flymake.py:76: SyntaxWarning: invalid escape sequence '\W' RE_NON_ALPHANUM = re.compile("\W", re.U) /usr/lib/python3/dist-packages/yapsy/__init___flymake.py:78: SyntaxWarning: invalid escape sequence '\W' RE_NON_ALPHANUM = re.compile("\W") Setting up python3-blinker (1.7.0-1) ... Setting up python3-daemonize (2.5.0-2) ... Setting up python3-pkg-resources (68.1.2-2) ... Setting up python3-distutils (3.11.5-1) ... python3.12: can't get files for byte-compilation Setting up python3-iniconfig (1.1.1-2) ... Setting up python3-waitress (2.1.2-2) ... Setting up python3-setuptools (68.1.2-2) ... Setting up python3-bottle (0.12.25-1) ... Setting up python3-colorama (0.4.6-4) ... Setting up python3-colorlog (6.8.2-1) ... Setting up python3-charset-normalizer (3.3.2-1) ... Setting up python3-pytest (7.4.4-3) ... Setting up python3-itsdangerous (2.1.2-4) ... Setting up python3-all (3.11.6-1) ... Setting up python3-click (8.1.7-1) ... Setting up python3-bs4 (4.12.3-1) ... Setting up python3-pygments (2.17.2+dfsg-1) ... Setting up python3-chardet (5.2.0+dfsg-1) ... Setting up python3-paste (3.9.0-1) ... Setting up python3-cryptography (41.0.7-4) ... Setting up python3-requests (2.31.0+dfsg-1) ... Setting up dh-python (6.20240401) ... Setting up python3-openssl (24.1.0-1) ... Setting up python3-flask (3.0.3-1) ... Setting up python3-pastedeploy (3.1-1) ... Setting up python3-webtest (3.0.0-3) ... Processing triggers for libc-bin (2.37-15) ... Processing triggers for ca-certificates (20240203) ... 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-2_source.changes dpkg-buildpackage: info: source package errbot dpkg-buildpackage: info: source version 6.2.0+ds-2 dpkg-buildpackage: info: source distribution unstable dpkg-buildpackage: info: source changed by Alexandre Detiste dpkg-source --before-build . dpkg-buildpackage: info: host architecture armhf debian/rules clean dh clean --buildsystem=pybuild dh_auto_clean -O--buildsystem=pybuild pybuild --clean -i python{version} -p "3.12 3.11" I: pybuild base:311: python3.12 setup.py clean 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.11 setup.py clean running clean removing '/build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build' (and everything under it) 'build/bdist.linux-armv7l' does not exist -- can't clean it 'build/scripts-3.11' 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.11" I: pybuild base:311: python3.12 setup.py config running config I: pybuild base:311: python3.11 setup.py config running config dh_auto_build -O--buildsystem=pybuild pybuild --build -i python{version} -p "3.12 3.11" I: pybuild base:311: /usr/bin/python3.12 setup.py build 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/flow.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/plugin_manager.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/__init__.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/templating.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/bootstrap.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/streaming.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/core.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/config-template.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/rendering copying ./errbot/rendering/ansiext.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/rendering copying ./errbot/rendering/__init__.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/rendering copying ./errbot/rendering/xhtmlim.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/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/flows.py -> /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/webserver.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/health.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/chatRoom.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/__init__.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/wsview.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/backup.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/plugins.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 creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/storage copying ./errbot/storage/base.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/storage copying ./errbot/storage/shelf.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/memory.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/storage creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/base.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/telegram_messenger.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/null.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/irc.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/__init__.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/text.py -> /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 copying ./errbot/backends/irc.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/text.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/xmpp.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends copying ./errbot/backends/telegram_messenger.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends 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-bg.svg -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends/images copying ./errbot/backends/images/errbot.svg -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/backends/images copying ./errbot/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/health.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/textcmds.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/plugins.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/help.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/backup.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/flows.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/acls.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/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/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/webstatus.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/status_plugins.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/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/status_gc.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/status_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/repos.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/about.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/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/plugin_info.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/core_plugins/templates copying ./errbot/storage/shelf.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/storage copying ./errbot/storage/memory.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/storage creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.12_errbot/build/errbot/templates 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 running build running build_py creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot copying ./errbot/utils.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot copying ./errbot/plugin_info.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot copying ./errbot/flow.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot copying ./errbot/logs.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot copying ./errbot/plugin_manager.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot copying ./errbot/plugin_wizard.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot copying ./errbot/__init__.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot copying ./errbot/version.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot copying ./errbot/templating.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot copying ./errbot/repo_manager.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot copying ./errbot/bootstrap.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot copying ./errbot/botplugin.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot copying ./errbot/streaming.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot copying ./errbot/backend_plugin_manager.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot copying ./errbot/core.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot copying ./errbot/cli.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot copying ./errbot/config-template.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/rendering copying ./errbot/rendering/ansiext.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/rendering copying ./errbot/rendering/__init__.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/rendering copying ./errbot/rendering/xhtmlim.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/rendering creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/help.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/flows.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/utils.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/webserver.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/textcmds.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/health.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/chatRoom.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/__init__.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/wsview.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/vcheck.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/backup.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/cnf_filter.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/plugins.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/acls.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/storage copying ./errbot/storage/base.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/storage copying ./errbot/storage/shelf.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/storage copying ./errbot/storage/__init__.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/storage copying ./errbot/storage/memory.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/storage creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends copying ./errbot/backends/base.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends copying ./errbot/backends/telegram_messenger.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends copying ./errbot/backends/test.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends copying ./errbot/backends/null.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends copying ./errbot/backends/xmpp.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends copying ./errbot/backends/irc.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends copying ./errbot/backends/__init__.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends copying ./errbot/backends/text.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends copying ./errbot/backends/null.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends copying ./errbot/backends/irc.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends copying ./errbot/backends/test.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends copying ./errbot/backends/text.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends copying ./errbot/backends/xmpp.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends copying ./errbot/backends/telegram_messenger.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends/styles copying ./errbot/backends/styles/style-demo.css -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends/styles copying ./errbot/backends/styles/style.css -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends/styles creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends/images copying ./errbot/backends/images/errbot-bg.svg -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends/images copying ./errbot/backends/images/errbot.svg -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends/images copying ./errbot/backends/images/prompt.svg -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/backends/images copying ./errbot/core_plugins/health.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/vcheck.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/textcmds.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/utils.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/plugins.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/cnf_filter.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/help.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/backup.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/flows.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/chatRoom.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/acls.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/webserver.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins copying ./errbot/core_plugins/test.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/repos2.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/webstatus.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_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.11_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/status.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_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.11_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.11_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/repos.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/about.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/core_plugins/templates copying ./errbot/core_plugins/templates/webserver.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_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.11_errbot/build/errbot/core_plugins/templates copying ./errbot/storage/shelf.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/storage copying ./errbot/storage/memory.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/storage creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/templates creating /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/templates/initdir copying ./errbot/templates/initdir/example.py -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/templates/initdir copying ./errbot/templates/initdir/example.plug -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/templates/initdir copying ./errbot/templates/initdir/config.py.tmpl -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/templates/initdir copying ./errbot/templates/card.md -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build/errbot/templates copying ./errbot/templates/new_plugin.py.tmpl -> /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_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.11" 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.2, pytest-7.4.4, pluggy-1.4.0 rootdir: /build/reproducible-path/errbot-6.2.0+ds collected 215 items / 8 deselected / 207 selected tests/backend_manager_test.py ... [ 1%] tests/base_backend_test.py ................................. [ 17%] tests/cascade_dependencies_test.py . [ 17%] tests/circular_dependencies_test.py . [ 18%] tests/commands_test.py .........EEEEEEEEEEEEEEEEEEE [ 31%] tests/core_plugins_test.py EEE [ 33%] tests/core_test.py EE [ 34%] tests/dependencies_test.py EEEEEEE [ 37%] tests/dynaplug_test.py EEEEE [ 40%] tests/flow_e2e_test.py EEEEEEEEEEEEE [ 46%] tests/flow_test.py ... [ 47%] tests/i18n_test.py EEEE [ 49%] tests/link_test.py E [ 50%] tests/matchall_test.py EE [ 51%] tests/md_rendering_test.py .... [ 53%] tests/mention_test.py EEE [ 54%] 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 [ 59%] tests/plugin_config_test.py .......E [ 63%] tests/plugin_info_test.py ........ [ 67%] tests/plugin_management_test.py ....... [ 71%] tests/poller_test.py EE [ 71%] tests/repo_manager_test.py ....... [ 75%] tests/simple_identifiers_test.py ..... [ 77%] tests/streaming_test.py F [ 78%] tests/syntax_test.py EEEE [ 80%] tests/templates_test.py EEEEE [ 82%] tests/utils_test.py .................... [ 92%] tests/webhooks_test.py EEEEEEEEEEEEEEE [ 99%] tests/backend_tests/text_test.py E [100%] ==================================== ERRORS ==================================== _______________________ ERROR at setup of test_status_gc _______________________ self = , processes = 10 initializer = None, initargs = (), maxtasksperchild = None, context = None def __init__(self, processes=None, initializer=None, initargs=(), maxtasksperchild=None, context=None): # Attributes initialized early to make sure that they exist in # __del__() if __init__() raises an exception self._pool = [] self._state = INIT self._ctx = context or get_context() self._setup_queues() self._taskqueue = queue.SimpleQueue() # The _change_notifier queue exist to wake up self._handle_workers() # when the cache (self._cache) is empty or when there is a change in # the _state variable of the thread that runs _handle_workers. self._change_notifier = self._ctx.SimpleQueue() self._cache = _PoolCache(notifier=self._change_notifier) self._maxtasksperchild = maxtasksperchild self._initializer = initializer self._initargs = initargs if processes is None: processes = os.cpu_count() or 1 if processes < 1: raise ValueError("Number of processes must be at least 1") if maxtasksperchild is not None: if not isinstance(maxtasksperchild, int) or maxtasksperchild <= 0: raise ValueError("maxtasksperchild must be a positive int or None") if initializer is not None and not callable(initializer): raise TypeError('initializer must be a callable') self._processes = processes try: > self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:215: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = , processes = 10 initializer = None, initargs = (), maxtasksperchild = None, context = None def __init__(self, processes=None, initializer=None, initargs=(), maxtasksperchild=None, context=None): # Attributes initialized early to make sure that they exist in # __del__() if __init__() raises an exception self._pool = [] self._state = INIT self._ctx = context or get_context() self._setup_queues() self._taskqueue = queue.SimpleQueue() # The _change_notifier queue exist to wake up self._handle_workers() # when the cache (self._cache) is empty or when there is a change in # the _state variable of the thread that runs _handle_workers. self._change_notifier = self._ctx.SimpleQueue() self._cache = _PoolCache(notifier=self._change_notifier) self._maxtasksperchild = maxtasksperchild self._initializer = initializer self._initargs = initargs if processes is None: processes = os.cpu_count() or 1 if processes < 1: raise ValueError("Number of processes must be at least 1") if maxtasksperchild is not None: if not isinstance(maxtasksperchild, int) or maxtasksperchild <= 0: raise ValueError("maxtasksperchild must be a positive int or None") if initializer is not None and not callable(initializer): raise TypeError('initializer must be a callable') self._processes = processes try: self._repopulate_pool() except Exception: for p in self._pool: if p.exitcode is None: > p.terminate() E AttributeError: 'DummyProcess' object has no attribute 'terminate' /usr/lib/python3.12/multiprocessing/pool.py:219: AttributeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, 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 ----------------------------- 2024-04-22 07:23:07,055 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:07,072 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:07,073 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:07,081 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:07,082 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:07,090 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 992, 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_config_cycle ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:08,113 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:08,124 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:08,124 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:08,132 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:08,132 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:08,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.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_apropos ________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:08,936 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:08,947 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:08,948 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:08,955 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:08,956 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:08,958 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_logtail ________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:09,735 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:09,746 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:09,747 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:09,754 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:09,755 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:09,757 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_history ________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:10,522 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:10,535 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:10,536 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:10,543 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:10,544 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:10,546 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________ ERROR at setup of test_encoding_preservation _________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:11,338 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:11,350 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:11,351 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:11,359 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:11,360 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:11,362 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________ ERROR at setup of test_webserver_webhook_test _________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:12,165 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:12,177 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:12,178 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:12,186 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:12,187 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:12,189 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________ ERROR at setup of test_activate_reload_and_deactivate _____________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:12,974 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:12,985 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:12,986 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:12,994 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:12,995 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:12,997 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________ ERROR at setup of test_unblacklist_and_blacklist _______________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:13,789 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:13,801 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:13,802 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:13,811 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:13,811 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:13,813 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_optional_prefix ____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:14,619 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:14,631 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:14,632 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:14,640 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:14,641 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:14,643 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________ ERROR at setup of test_optional_prefix_re_cmd _________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:15,416 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:15,428 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:15,429 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:15,438 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:15,438 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:15,440 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________________ ERROR at setup of test_simple_match ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:16,447 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:16,459 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:16,460 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:16,468 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:16,469 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:16,471 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________ ERROR at setup of test_no_suggest_on_re_commands _______________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:17,267 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:17,279 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:17,280 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:17,291 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:17,292 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:17,294 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_callback_no_command __________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:18,091 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:18,103 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:18,103 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:18,112 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:18,113 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:18,115 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_subcommands ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:18,927 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:18,939 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:18,940 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:18,949 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:18,950 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:18,952 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______ ERROR at setup of test_command_not_found_with_space_in_bot_prefix _______ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:19,749 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:19,761 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:19,762 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:19,773 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:19,774 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:19,776 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_mock_injection _____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:20,566 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:20,578 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:20,578 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:20,587 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:20,588 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:20,590 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_multiline_command ___________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:21,396 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:21,408 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:21,409 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:21,418 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:21,419 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:21,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 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_plugin_info_command __________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:22,210 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:22,223 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:22,223 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:22,235 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:22,235 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:22,237 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_help_is_still_here ___________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- DEBUG errbot.backends.test Merging {'CORE_PLUGINS': ('Help', 'Utils', 'CommandNotFoundFilter'), 'BOT_ALT_PREFIXES': ('!',), 'BOT_PREFIX': '$'} to the bot config. INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:23,031 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:23,043 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:23,044 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:23,053 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:23,054 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:23,056 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_echo_still_here ____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- DEBUG errbot.backends.test Merging {'CORE_PLUGINS': ('Help', 'Utils', 'CommandNotFoundFilter'), 'BOT_ALT_PREFIXES': ('!',), 'BOT_PREFIX': '$'} to the bot config. INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:23,869 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:23,881 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:23,882 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:23,892 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:23,893 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:23,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 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_bot_prefix_replaced __________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- DEBUG errbot.backends.test Merging {'CORE_PLUGINS': ('Help', 'Utils', 'CommandNotFoundFilter'), 'BOT_ALT_PREFIXES': ('!',), 'BOT_PREFIX': '$'} to the bot config. INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:24,693 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:24,705 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:24,706 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:24,718 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:24,718 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:24,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 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_admins_to_notify ____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- DEBUG errbot.backends.test Merging {'BOT_ADMINS_NOTIFICATIONS': 'zoni@localdomain'} to the bot config. INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:25,510 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:25,522 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:25,522 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:25,532 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:25,532 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:25,534 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_admins_not_notified __________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- DEBUG errbot.backends.test Merging {'BOT_ADMINS_NOTIFICATIONS': 'zoni@localdomain'} to the bot config. INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:26,322 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:26,334 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:26,335 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:26,344 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:26,345 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:26,347 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________ ERROR at setup of test_if_all_loaded_by_default ________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:27,126 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:27,138 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:27,139 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:27,151 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:27,151 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:27,153 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_single_dependency ___________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:27,927 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:27,939 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:27,940 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:27,949 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:27,950 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:27,952 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_double_dependency ___________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:28,734 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:28,746 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:28,747 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:28,756 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:28,757 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:28,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 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________ ERROR at setup of test_dependency_retrieval __________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:29,749 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:29,760 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:29,761 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:29,770 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:29,771 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:29,773 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________ ERROR at setup of test_direct_circular_dependency _______________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:30,529 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:30,540 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:30,541 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:30,550 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:30,551 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:30,553 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________ ERROR at setup of test_indirect_circular_dependency ______________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:31,308 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:31,320 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:31,321 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:31,330 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:31,331 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:31,333 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_chained_dependency ___________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:32,089 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:32,100 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:32,101 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:32,110 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:32,111 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:32,113 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_simple _________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:32,903 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:32,915 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:32,915 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:32,926 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:32,926 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:32,929 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________________ ERROR at setup of test_arg __________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:33,686 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:33,698 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:33,698 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:33,709 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:33,709 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:33,714 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________________ ERROR at setup of test_re ___________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:34,483 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:34,495 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:34,496 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:34,506 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:34,507 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:34,509 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________________ ERROR at setup of test_saw __________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:35,299 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:35,311 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:35,312 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:35,322 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:35,323 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:35,325 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_clashing ________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:36,113 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:36,125 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:36,125 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:36,136 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:36,137 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:36,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 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_list_flows _______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:36,904 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:36,915 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:36,916 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:36,929 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:36,930 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:36,931 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_no_autotrigger _____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:37,681 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:37,693 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:37,693 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:37,704 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:37,704 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:37,706 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_autotrigger ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:38,464 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:38,476 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:38,476 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:38,487 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:38,487 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:38,489 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________ ERROR at setup of test_no_duplicate_autotrigger ________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:39,261 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:39,272 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:39,273 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:39,283 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:39,284 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:39,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 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________ ERROR at setup of test_secondary_autotrigger _________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:40,044 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:40,055 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:40,056 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:40,070 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:40,070 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:40,072 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_manual_flow ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:40,833 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:40,845 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:40,846 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:40,857 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:40,858 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:40,860 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________ ERROR at setup of test_manual_flow_with_or_without_hinting __________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:41,854 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:41,866 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:41,867 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:41,879 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:41,879 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:41,881 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________ ERROR at setup of test_no_flyby_trigger_flow _________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:42,679 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:42,691 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:42,692 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:42,703 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:42,704 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:42,706 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_flow_only _______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:43,466 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:43,478 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:43,478 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:43,489 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:43,490 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:43,492 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_flow_only_help _____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:44,254 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:44,265 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:44,266 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:44,277 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:44,277 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:44,279 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_flows_stop _______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:45,037 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:45,048 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:45,049 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:45,060 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:45,061 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:45,063 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_flows_kill _______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:45,861 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:45,873 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:45,873 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:45,885 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:45,886 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:45,888 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_room_flow _______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:46,650 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:46,662 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:46,663 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:46,675 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:46,676 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:46,678 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_i18n_return ______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:47,477 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:47,489 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:47,490 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:47,501 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:47,502 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:47,504 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_i18n_simple_name ____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:48,282 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:48,294 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:48,295 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:48,307 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:48,307 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:48,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.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_i18n_prefix ______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:49,098 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:49,110 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:49,111 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:49,123 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:49,124 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:49,126 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_i18n_suffix ______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:49,891 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:49,903 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:49,904 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:49,916 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:49,917 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:49,919 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_linked_plugin_here ___________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:50,708 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:50,721 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:50,721 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:50,734 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:50,735 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:50,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.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_botmatch_correct ____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:51,534 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:51,547 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:51,548 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:51,561 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:51,561 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:51,564 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_botmatch ________________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:52,377 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:52,390 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:52,391 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:52,404 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:52,405 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:52,407 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_foreign_mention ____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:53,232 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:53,246 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:53,247 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:53,263 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:53,264 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:53,266 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_testbot_mention ____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:54,322 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:54,335 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:54,335 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:54,348 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:54,349 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:54,351 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_multiple_mentions ___________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:55,156 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:55,168 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:55,169 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:55,182 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:55,183 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:55,185 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_plugin_methods _____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:55,998 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:56,010 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:56,011 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:56,023 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:56,024 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:56,026 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________ ERROR at setup of test_create_join_leave_destroy_lifecycle __________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:56,809 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:56,821 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:56,822 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:56,836 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:56,837 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:56,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.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_occupants _______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:57,618 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:57,630 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:57,631 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:57,643 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:57,644 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:57,646 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________________ ERROR at setup of test_topic _________________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:58,426 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:58,438 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:58,439 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:58,451 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:58,452 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:58,454 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_plugin_callbacks ____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:23:59,248 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:23:59,260 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:23:59,260 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:23:59,273 DEBUG errbot.core ErrBot init. 2024-04-22 07:23:59,273 DEBUG errbot.backends.base Backend init. 2024-04-22 07:23:59,275 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_botcommands ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:00,038 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:00,050 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:00,050 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:00,063 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:00,063 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:00,065 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________________ ERROR at setup of test_first _________________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:00,839 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:00,851 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:00,852 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:00,864 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:00,865 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:00,867 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_second _________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:01,645 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:01,656 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:01,657 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:01,669 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:01,670 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:01,671 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________________ ERROR at setup of test_failed_config _____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:02,482 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:02,494 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:02,495 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:02,507 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:02,508 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:02,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.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________________ ERROR at setup of test_failed_config _____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:03,312 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:03,324 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:03,325 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:03,337 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:03,338 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:03,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.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________________ ERROR at setup of test_delayed_hello _____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:04,330 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:04,342 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:04,343 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:04,355 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:04,356 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:04,358 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_delayed_hello_loop ___________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:05,125 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:05,137 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:05,138 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:05,150 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:05,151 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:05,153 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_nosyntax ________________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:06,537 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:06,550 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:06,550 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:06,563 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:06,564 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:06,566 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_syntax _________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:07,371 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:07,383 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:07,384 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:07,396 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:07,397 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:07,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.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_re_syntax _______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:08,168 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:08,180 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:08,181 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:08,196 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:08,196 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:08,198 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_arg_syntax _______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:08,967 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:08,979 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:08,979 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:08,992 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:08,993 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:08,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.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_1 ______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:09,782 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:09,794 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:09,795 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:09,807 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:09,808 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:09,810 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_2 ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:10,603 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:10,615 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:10,616 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:10,628 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:10,629 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:10,631 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_3 ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:11,399 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:11,411 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:11,412 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:11,427 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:11,428 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:11,430 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_4 ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:12,199 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:12,212 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:12,212 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:12,225 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:12,226 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:12,228 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_5 ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:13,024 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:13,037 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:13,038 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:13,051 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:13,052 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:13,054 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________ ERROR at setup of test_not_configured_url_returns_404 _____________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:14,015 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:14,029 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:14,029 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:14,043 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:14,043 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:14,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 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_webserver_plugin_ok __________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:14,860 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:14,873 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:14,874 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:14,887 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:14,888 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:14,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 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________ ERROR at setup of test_trailing_no_slash_ok __________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:15,675 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:15,687 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:15,688 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:15,702 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:15,703 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:15,705 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________ ERROR at setup of test_trailing_slash_also_ok _________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:16,482 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:16,495 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:16,495 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:16,509 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:16,509 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:16,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 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________ ERROR at setup of test_json_is_automatically_decoded _____________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:17,300 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:17,312 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:17,313 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:17,326 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:17,327 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:17,329 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______ ERROR at setup of test_json_on_custom_url_is_automatically_decoded ______ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:18,137 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:18,149 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:18,150 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:18,475 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:18,476 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:18,478 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _ ERROR at setup of test_post_form_on_webhook_without_form_param_is_automatically_decoded _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:19,280 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:19,293 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:19,294 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:19,306 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:19,307 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:19,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.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _ ERROR at setup of test_post_form_on_webhook_with_custom_url_and_without_form_param_is_automatically_decoded _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:20,133 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:20,145 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:20,146 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:20,159 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:20,160 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:20,162 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _ ERROR at setup of test_webhooks_with_form_parameter_decode_json_automatically _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:20,982 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:20,994 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:20,995 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:21,007 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:21,008 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:21,010 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _ ERROR at setup of test_webhooks_with_form_parameter_on_custom_url_decode_json_automatically _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:21,805 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:21,818 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:21,818 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:21,833 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:21,834 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:21,836 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________ ERROR at setup of test_webhooks_with_raw_request _______________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:22,628 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:22,640 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:22,641 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:22,654 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:22,655 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:22,657 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______ ERROR at setup of test_webhooks_with_naked_decorator_raw_request _______ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:23,469 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:23,481 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:23,482 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:23,495 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:23,496 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:23,498 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______ ERROR at setup of test_generate_certificate_creates_usable_cert ________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:24,320 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:24,333 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:24,334 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:24,346 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:24,347 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:24,350 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________ ERROR at setup of test_custom_headers_and_status_codes ____________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:25,143 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:25,155 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:25,156 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:25,172 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:25,173 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:25,175 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_lambda_webhook _____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:25,975 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:25,987 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:25,988 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:26,001 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:26,001 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:26,003 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.12/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.12/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.12/threading.py", line 992, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_change_presence ____________________ @pytest.fixture def text_backend(): tempdir = mkdtemp() # reset the config every time sys.modules.pop("errbot.config-template", None) __import__("errbot.config-template") config = sys.modules["errbot.config-template"] bot_config_defaults(config) config.BOT_DATA_DIR = tempdir config.BOT_LOG_FILE = os.path.join(tempdir, "log.txt") config.BOT_EXTRA_PLUGIN_DIR = [] config.BOT_LOG_LEVEL = logging.DEBUG config.BOT_PREFIX = "!" config.BOT_IDENTITY["username"] = "@testme" config.BOT_ADMINS = ["@test_admin"] > return TextBackend(config) tests/backend_tests/text_test.py:29: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/text.py:211: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.12/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.12/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.12/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.12/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.12/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: RuntimeError ---------------------------- Captured stdout setup ----------------------------- DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:26,801 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:26,802 DEBUG errbot.backends.base Backend init. ------------------------------ Captured log setup ------------------------------ DEBUG errbot.core:core.py:48 ErrBot init. DEBUG errbot.backends.base:base.py:809 Backend init. =================================== FAILURES =================================== ________________________________ test_streaming ________________________________ def test_streaming(): canary = b"this is my test" * 1000 source = Stream(TestPerson("gbin@gootz.net"), BytesIO(canary)) clients = [StreamingClient() for _ in range(50)] > Tee(source, clients).run() tests/streaming_test.py:17: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/streaming.py:81: in run thread.start() _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.12/threading.py:992: 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_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 ERROR tests/backend_tests/text_test.py::test_change_presence - RuntimeError: ... = 1 failed, 110 passed, 8 deselected, 2 warnings, 96 errors in 95.14s (0:01:35) = 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.11_errbot/build; python3.11 -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.11.8, pytest-7.4.4, pluggy-1.4.0 rootdir: /build/reproducible-path/errbot-6.2.0+ds collected 215 items / 8 deselected / 207 selected tests/backend_manager_test.py ... [ 1%] tests/base_backend_test.py ................................. [ 17%] tests/cascade_dependencies_test.py . [ 17%] tests/circular_dependencies_test.py . [ 18%] tests/commands_test.py .........EEEEEEEEEEEEEEEEEEE [ 31%] tests/core_plugins_test.py EEE [ 33%] tests/core_test.py EE [ 34%] tests/dependencies_test.py EEEEEEE [ 37%] tests/dynaplug_test.py EEEEE [ 40%] tests/flow_e2e_test.py EEEEEEEEEEEEE [ 46%] tests/flow_test.py ... [ 47%] tests/i18n_test.py EEEE [ 49%] tests/link_test.py E [ 50%] tests/matchall_test.py EE [ 51%] tests/md_rendering_test.py .... [ 53%] tests/mention_test.py EEE [ 54%] 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 [ 59%] tests/plugin_config_test.py .......E [ 63%] tests/plugin_info_test.py ........ [ 67%] tests/plugin_management_test.py ....... [ 71%] tests/poller_test.py EE [ 71%] tests/repo_manager_test.py ....... [ 75%] tests/simple_identifiers_test.py ..... [ 77%] tests/streaming_test.py F [ 78%] tests/syntax_test.py EEEE [ 80%] tests/templates_test.py EEEEE [ 82%] tests/utils_test.py .................... [ 92%] tests/webhooks_test.py EEEEEEEEEEEEEEE [ 99%] tests/backend_tests/text_test.py E [100%] ==================================== ERRORS ==================================== _______________________ ERROR at setup of test_status_gc _______________________ self = , processes = 10 initializer = None, initargs = (), maxtasksperchild = None, context = None def __init__(self, processes=None, initializer=None, initargs=(), maxtasksperchild=None, context=None): # Attributes initialized early to make sure that they exist in # __del__() if __init__() raises an exception self._pool = [] self._state = INIT self._ctx = context or get_context() self._setup_queues() self._taskqueue = queue.SimpleQueue() # The _change_notifier queue exist to wake up self._handle_workers() # when the cache (self._cache) is empty or when there is a change in # the _state variable of the thread that runs _handle_workers. self._change_notifier = self._ctx.SimpleQueue() self._cache = _PoolCache(notifier=self._change_notifier) self._maxtasksperchild = maxtasksperchild self._initializer = initializer self._initargs = initargs if processes is None: processes = os.cpu_count() or 1 if processes < 1: raise ValueError("Number of processes must be at least 1") if maxtasksperchild is not None: if not isinstance(maxtasksperchild, int) or maxtasksperchild <= 0: raise ValueError("maxtasksperchild must be a positive int or None") if initializer is not None and not callable(initializer): raise TypeError('initializer must be a callable') self._processes = processes try: > self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:215: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = , processes = 10 initializer = None, initargs = (), maxtasksperchild = None, context = None def __init__(self, processes=None, initializer=None, initargs=(), maxtasksperchild=None, context=None): # Attributes initialized early to make sure that they exist in # __del__() if __init__() raises an exception self._pool = [] self._state = INIT self._ctx = context or get_context() self._setup_queues() self._taskqueue = queue.SimpleQueue() # The _change_notifier queue exist to wake up self._handle_workers() # when the cache (self._cache) is empty or when there is a change in # the _state variable of the thread that runs _handle_workers. self._change_notifier = self._ctx.SimpleQueue() self._cache = _PoolCache(notifier=self._change_notifier) self._maxtasksperchild = maxtasksperchild self._initializer = initializer self._initargs = initargs if processes is None: processes = os.cpu_count() or 1 if processes < 1: raise ValueError("Number of processes must be at least 1") if maxtasksperchild is not None: if not isinstance(maxtasksperchild, int) or maxtasksperchild <= 0: raise ValueError("maxtasksperchild must be a positive int or None") if initializer is not None and not callable(initializer): raise TypeError('initializer must be a callable') self._processes = processes try: self._repopulate_pool() except Exception: for p in self._pool: if p.exitcode is None: > p.terminate() E AttributeError: 'DummyProcess' object has no attribute 'terminate' /usr/lib/python3.11/multiprocessing/pool.py:219: AttributeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, 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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 219, in __init__ p.terminate() ^^^^^^^^^^^ AttributeError: 'DummyProcess' object has no attribute 'terminate' ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:48,745 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:48,764 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:48,765 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:48,777 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:48,778 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:48,789 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, 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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 219, in __init__ p.terminate() ^^^^^^^^^^^ AttributeError: 'DummyProcess' object has no attribute 'terminate' _____________________ ERROR at setup of test_config_cycle ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:50,089 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:50,102 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:50,102 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:50,110 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:50,111 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:50,142 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_apropos ________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:50,952 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:50,965 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:50,966 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:50,974 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:50,974 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:50,976 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_logtail ________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:51,758 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:51,770 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:51,771 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:51,779 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:51,780 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:51,782 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_history ________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:52,737 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:52,749 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:52,749 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:52,757 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:52,758 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:52,760 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________ ERROR at setup of test_encoding_preservation _________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:53,545 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:53,557 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:53,558 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:53,566 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:53,566 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:53,569 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________ ERROR at setup of test_webserver_webhook_test _________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:54,360 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:54,372 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:54,373 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:54,381 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:54,382 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:54,384 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________ ERROR at setup of test_activate_reload_and_deactivate _____________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:55,345 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:55,357 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:55,358 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:55,366 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:55,367 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:55,369 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________ ERROR at setup of test_unblacklist_and_blacklist _______________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:56,165 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:56,177 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:56,178 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:56,186 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:56,187 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:56,189 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_optional_prefix ____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:56,981 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:56,993 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:56,994 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:57,002 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:57,002 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:57,004 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________ ERROR at setup of test_optional_prefix_re_cmd _________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:57,954 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:57,966 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:57,967 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:57,975 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:57,976 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:57,978 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________________ ERROR at setup of test_simple_match ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:58,766 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:58,778 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:58,779 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:58,787 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:58,788 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:58,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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________ ERROR at setup of test_no_suggest_on_re_commands _______________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:24:59,573 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:24:59,585 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:24:59,585 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:24:59,594 DEBUG errbot.core ErrBot init. 2024-04-22 07:24:59,595 DEBUG errbot.backends.base Backend init. 2024-04-22 07:24:59,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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_callback_no_command __________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:00,531 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:00,543 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:00,544 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:00,553 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:00,553 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:00,555 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_subcommands ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:01,334 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:01,346 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:01,347 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:01,356 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:01,357 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:01,359 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______ ERROR at setup of test_command_not_found_with_space_in_bot_prefix _______ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:02,327 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:02,339 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:02,340 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:02,349 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:02,349 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:02,352 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_mock_injection _____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:03,136 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:03,149 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:03,150 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:03,158 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:03,159 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:03,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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_multiline_command ___________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:03,942 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:03,954 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:03,955 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:03,964 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:03,965 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:03,967 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_plugin_info_command __________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:04,937 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:04,949 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:04,950 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:04,959 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:04,960 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:04,962 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_help_is_still_here ___________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- DEBUG errbot.backends.test Merging {'CORE_PLUGINS': ('Help', 'Utils', 'CommandNotFoundFilter'), 'BOT_ALT_PREFIXES': ('!',), 'BOT_PREFIX': '$'} to the bot config. INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:05,752 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:05,764 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:05,765 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:05,774 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:05,775 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:05,777 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_echo_still_here ____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- DEBUG errbot.backends.test Merging {'CORE_PLUGINS': ('Help', 'Utils', 'CommandNotFoundFilter'), 'BOT_ALT_PREFIXES': ('!',), 'BOT_PREFIX': '$'} to the bot config. INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:06,557 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:06,569 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:06,570 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:06,579 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:06,580 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:06,582 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_bot_prefix_replaced __________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- DEBUG errbot.backends.test Merging {'CORE_PLUGINS': ('Help', 'Utils', 'CommandNotFoundFilter'), 'BOT_ALT_PREFIXES': ('!',), 'BOT_PREFIX': '$'} to the bot config. INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:07,545 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:07,558 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:07,558 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:07,568 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:07,568 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:07,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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_admins_to_notify ____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- DEBUG errbot.backends.test Merging {'BOT_ADMINS_NOTIFICATIONS': 'zoni@localdomain'} to the bot config. INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:08,348 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:08,361 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:08,361 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:08,370 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:08,371 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:08,373 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_admins_not_notified __________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- DEBUG errbot.backends.test Merging {'BOT_ADMINS_NOTIFICATIONS': 'zoni@localdomain'} to the bot config. INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:09,151 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:09,163 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:09,164 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:09,173 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:09,174 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:09,176 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________ ERROR at setup of test_if_all_loaded_by_default ________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:10,133 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:10,145 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:10,148 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:10,157 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:10,158 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:10,160 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_single_dependency ___________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:10,940 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:10,952 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:10,953 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:10,962 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:10,963 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:10,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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_double_dependency ___________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:11,744 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:11,756 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:11,757 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:11,766 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:11,767 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:11,769 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________ ERROR at setup of test_dependency_retrieval __________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:12,719 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:12,732 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:12,733 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:12,742 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:12,743 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:12,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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________ ERROR at setup of test_direct_circular_dependency _______________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:13,531 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:13,544 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:13,544 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:13,554 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:13,555 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:13,557 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________ ERROR at setup of test_indirect_circular_dependency ______________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:14,339 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:14,352 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:14,352 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:14,527 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:14,528 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:14,530 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_chained_dependency ___________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:15,323 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:15,336 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:15,337 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:15,346 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:15,347 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:15,350 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_simple _________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:16,125 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:16,138 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:16,139 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:16,149 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:16,150 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:16,152 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________________ ERROR at setup of test_arg __________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:17,150 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:17,175 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:17,177 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:17,198 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:17,201 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:17,206 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________________ ERROR at setup of test_re ___________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:17,990 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:18,003 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:18,004 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:18,014 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:18,015 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:18,017 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________________ ERROR at setup of test_saw __________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:18,793 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:18,806 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:18,806 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:18,817 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:18,818 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:18,820 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_clashing ________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:19,795 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:19,807 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:19,808 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:19,819 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:19,819 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:19,821 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_list_flows _______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:20,601 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:20,613 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:20,614 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:20,625 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:20,625 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:20,627 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_no_autotrigger _____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:21,406 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:21,418 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:21,419 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:21,429 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:21,430 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:21,432 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_autotrigger ______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:22,401 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:22,414 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:22,414 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:22,425 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:22,426 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:22,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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________ ERROR at setup of test_no_duplicate_autotrigger ________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:23,206 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:23,219 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:23,220 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:23,231 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:23,232 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:23,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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________ ERROR at setup of test_secondary_autotrigger _________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:24,011 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:24,023 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:24,024 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:24,035 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:24,036 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:24,038 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_manual_flow ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:24,997 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:25,009 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:25,010 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:25,021 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:25,022 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:25,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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________ ERROR at setup of test_manual_flow_with_or_without_hinting __________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:25,801 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:25,814 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:25,815 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:25,826 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:25,826 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:25,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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________ ERROR at setup of test_no_flyby_trigger_flow _________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:26,609 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:26,621 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:26,622 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:26,633 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:26,634 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:26,637 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_flow_only _______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:27,612 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:27,625 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:27,625 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:27,636 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:27,637 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:27,639 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_flow_only_help _____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:28,416 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:28,428 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:28,429 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:28,440 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:28,441 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:28,443 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_flows_stop _______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:29,437 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:29,450 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:29,450 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:29,462 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:29,462 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:29,465 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_flows_kill _______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:30,250 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:30,263 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:30,263 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:30,275 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:30,275 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:30,278 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_room_flow _______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:31,059 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:31,072 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:31,073 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:31,084 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:31,085 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:31,087 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_i18n_return ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:32,104 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:32,116 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:32,117 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:32,128 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:32,129 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:32,131 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_i18n_simple_name ____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:32,913 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:32,926 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:32,927 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:32,938 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:32,939 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:32,941 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_i18n_prefix ______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:33,714 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:33,727 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:33,727 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:33,739 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:33,740 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:33,742 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_i18n_suffix ______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:34,736 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:34,748 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:34,749 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:34,762 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:34,763 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:34,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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_linked_plugin_here ___________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:35,545 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:35,558 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:35,559 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:35,571 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:35,572 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:35,574 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_botmatch_correct ____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:36,349 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:36,362 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:36,363 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:36,375 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:36,376 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:36,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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_botmatch ________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:37,402 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:37,415 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:37,415 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:37,427 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:37,428 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:37,430 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_foreign_mention ____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:38,257 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:38,271 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:38,272 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:38,283 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:38,284 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:38,288 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_testbot_mention ____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:39,067 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:39,080 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:39,081 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:39,093 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:39,093 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:39,096 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_multiple_mentions ___________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:40,113 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:40,126 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:40,127 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:40,138 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:40,139 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:40,141 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_plugin_methods _____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:40,928 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:40,941 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:40,942 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:40,954 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:40,955 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:40,957 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________ ERROR at setup of test_create_join_leave_destroy_lifecycle __________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:41,993 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:42,006 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:42,006 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:42,018 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:42,019 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:42,021 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_occupants _______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:42,812 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:42,825 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:42,825 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:42,837 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:42,838 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:42,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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________________ ERROR at setup of test_topic _________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:43,628 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:43,641 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:43,641 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:43,653 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:43,654 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:43,656 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ___________________ ERROR at setup of test_plugin_callbacks ____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:44,692 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:44,705 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:44,705 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:44,718 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:44,718 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:44,720 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_botcommands ______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:45,522 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:45,535 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:45,536 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:45,548 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:45,549 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:45,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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________________ ERROR at setup of test_first _________________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:46,331 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:46,343 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:46,344 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:46,356 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:46,357 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:46,359 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_second _________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:47,394 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:47,407 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:47,408 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:47,421 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:47,422 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:47,424 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________________ ERROR at setup of test_failed_config _____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:48,230 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:48,243 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:48,244 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:48,256 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:48,257 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25: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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________________ ERROR at setup of test_failed_config _____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:49,098 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:49,111 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:49,112 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:49,124 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:49,125 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:49,127 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________________ ERROR at setup of test_delayed_hello _____________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:50,496 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:50,518 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:50,519 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:50,540 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:50,541 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:50,544 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_delayed_hello_loop ___________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:51,332 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:51,345 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:51,346 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:51,358 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:51,359 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:51,361 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_nosyntax ________________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:52,843 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:52,857 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:52,857 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:52,870 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:52,871 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:52,873 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________________ ERROR at setup of test_syntax _________________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:53,662 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:53,675 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:53,676 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:53,688 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:53,689 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:53,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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________________ ERROR at setup of test_re_syntax _______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:54,481 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:54,493 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:54,494 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:54,506 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:54,507 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:54,509 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_arg_syntax _______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:55,537 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:55,549 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:55,549 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:55,561 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:55,562 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:55,563 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_1 ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:56,346 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:56,358 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:56,359 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:56,371 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:56,371 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:56,374 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_2 ______________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:57,150 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:57,162 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:57,163 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:57,175 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:57,175 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:57,177 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_3 ______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:58,210 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:58,222 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:58,223 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:58,235 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:58,236 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:58,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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_4 ______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:25:59,027 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:25:59,040 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:25:59,041 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:25:59,054 DEBUG errbot.core ErrBot init. 2024-04-22 07:25:59,054 DEBUG errbot.backends.base Backend init. 2024-04-22 07:25:59,057 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______________________ ERROR at setup of test_templates_5 ______________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:26:00,126 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:26:00,139 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:26:00,140 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:26:00,153 DEBUG errbot.core ErrBot init. 2024-04-22 07:26:00,153 DEBUG errbot.backends.base Backend init. 2024-04-22 07:26:00,155 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________ ERROR at setup of test_not_configured_url_returns_404 _____________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:26:01,093 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:26:01,107 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:26:01,107 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:26:01,119 DEBUG errbot.core ErrBot init. 2024-04-22 07:26:01,119 DEBUG errbot.backends.base Backend init. 2024-04-22 07:26:01,122 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread __________________ ERROR at setup of test_webserver_plugin_ok __________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:26:01,923 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:26:01,936 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:26:01,937 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:26:01,949 DEBUG errbot.core ErrBot init. 2024-04-22 07:26:01,950 DEBUG errbot.backends.base Backend init. 2024-04-22 07:26:01,952 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _________________ ERROR at setup of test_trailing_no_slash_ok __________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:26:03,011 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:26:03,024 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:26:03,024 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:26:03,037 DEBUG errbot.core ErrBot init. 2024-04-22 07:26:03,037 DEBUG errbot.backends.base Backend init. 2024-04-22 07:26:03,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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ________________ ERROR at setup of test_trailing_slash_also_ok _________________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:26:03,841 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:26:03,854 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:26:03,854 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:26:03,867 DEBUG errbot.core ErrBot init. 2024-04-22 07:26:03,867 DEBUG errbot.backends.base Backend init. 2024-04-22 07:26:03,870 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _____________ ERROR at setup of test_json_is_automatically_decoded _____________ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:26:04,664 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:26:04,677 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:26:04,678 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:26:04,690 DEBUG errbot.core ErrBot init. 2024-04-22 07:26:04,691 DEBUG errbot.backends.base Backend init. 2024-04-22 07:26:04,693 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ______ ERROR at setup of test_json_on_custom_url_is_automatically_decoded ______ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:26:05,745 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:26:05,757 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:26:05,758 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:26:05,770 DEBUG errbot.core ErrBot init. 2024-04-22 07:26:05,771 DEBUG errbot.backends.base Backend init. 2024-04-22 07:26:05,773 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _ ERROR at setup of test_post_form_on_webhook_without_form_param_is_automatically_decoded _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:26:06,576 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:26:06,589 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:26:06,590 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:26:06,602 DEBUG errbot.core ErrBot init. 2024-04-22 07:26:06,603 DEBUG errbot.backends.base Backend init. 2024-04-22 07:26:06,605 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _ ERROR at setup of test_post_form_on_webhook_with_custom_url_and_without_form_param_is_automatically_decoded _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:26:07,401 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:26:07,414 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:26:07,415 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:26:07,427 DEBUG errbot.core ErrBot init. 2024-04-22 07:26:07,428 DEBUG errbot.backends.base Backend init. 2024-04-22 07:26:07,430 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _ ERROR at setup of test_webhooks_with_form_parameter_decode_json_automatically _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:26:08,485 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:26:08,499 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:26:08,499 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:26:08,512 DEBUG errbot.core ErrBot init. 2024-04-22 07:26:08,513 DEBUG errbot.backends.base Backend init. 2024-04-22 07:26:08,515 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _ ERROR at setup of test_webhooks_with_form_parameter_on_custom_url_decode_json_automatically _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:26:09,307 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:26:09,320 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:26:09,320 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:26:09,333 DEBUG errbot.core ErrBot init. 2024-04-22 07:26:09,334 DEBUG errbot.backends.base Backend init. 2024-04-22 07:26:09,336 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______________ ERROR at setup of test_webhooks_with_raw_request _______________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:26:10,400 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:26:10,413 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:26:10,413 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:26:10,426 DEBUG errbot.core ErrBot init. 2024-04-22 07:26:10,427 DEBUG errbot.backends.base Backend init. 2024-04-22 07:26:10,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.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______ ERROR at setup of test_webhooks_with_naked_decorator_raw_request _______ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = , restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:26:11,219 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:26:11,231 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:26:11,232 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:26:11,244 DEBUG errbot.core ErrBot init. 2024-04-22 07:26:11,245 DEBUG errbot.backends.base Backend init. 2024-04-22 07:26:11,247 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread _______ ERROR at setup of test_generate_certificate_creates_usable_cert ________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:26:12,047 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:26:12,059 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:26:12,060 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:26:12,073 DEBUG errbot.core ErrBot init. 2024-04-22 07:26:12,073 DEBUG errbot.backends.base Backend init. 2024-04-22 07:26:12,075 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________ ERROR at setup of test_custom_headers_and_status_codes ____________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:26:13,199 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:26:13,212 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:26:13,213 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:26:13,225 DEBUG errbot.core ErrBot init. 2024-04-22 07:26:13,226 DEBUG errbot.backends.base Backend init. 2024-04-22 07:26:13,228 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_lambda_webhook _____________________ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: > bot = backendpm.load_plugin() ../../../errbot/bootstrap.py:186: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backend_plugin_manager.py:72: in load_plugin return clazz(self._config) ../../../errbot/backends/test.py:273: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError During handling of the above exception, another exception occurred: request = > @pytest.fixture def testbot(request) -> TestBot: """ Pytest fixture to write tests against a fully functioning bot. For example, if you wanted to test the builtin `!about` command, you could write a test file with the following:: def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() It's possible to provide additional configuration to this fixture, by setting variables at module level or as class attributes (the latter taking precedence over the former). For example:: extra_plugin_dir = '/foo/bar' def test_about(testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..or:: extra_plugin_dir = '/foo/bar' class Tests: # Wins over `extra_plugin_dir = '/foo/bar'` above extra_plugin_dir = '/foo/baz' def test_about(self, testbot): testbot.push_message('!about') assert "Err version" in testbot.pop_message() ..to load additional plugins from the directory `/foo/bar` or `/foo/baz` respectively. This works for the following items, which are passed to the constructor of :class:`~errbot.backends.test.TestBot`: * `extra_plugin_dir` * `loglevel` """ def on_finish() -> TestBot: bot.stop() # setup the logging to something digestable. logger = logging.getLogger("") logging.getLogger("MARKDOWN").setLevel( logging.ERROR ) # this one is way too verbose in debug logger.setLevel(logging.DEBUG) console_hdlr = logging.StreamHandler(sys.stdout) console_hdlr.setFormatter( logging.Formatter("%(levelname)-8s %(name)-25s %(message)s") ) logger.handlers = [] logger.addHandler(console_hdlr) kwargs = {} for attr, default in ( ("extra_plugin_dir", None), ("extra_config", None), ("loglevel", logging.DEBUG), ): if hasattr(request, "instance"): kwargs[attr] = getattr(request.instance, attr, None) if kwargs[attr] is None: kwargs[attr] = getattr(request.module, attr, default) bot = TestBot(**kwargs) > bot.start() ../../../errbot/backends/test.py:705: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/test.py:482: in start self._bot = setup_bot("Test", self.logger, self.bot_config) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ backend_name = 'Test', logger = config = restore = None def setup_bot( backend_name: str, logger: logging.Logger, config: object, restore: Optional[str] = None, ) -> ErrBot: # from here the environment is supposed to be set (daemon / non daemon, # config.py in the python path ) bot_config_defaults(config) if hasattr(config, "BOT_LOG_FORMATTER"): format_logs(formatter=config.BOT_LOG_FORMATTER) else: format_logs(theme_color=config.TEXT_COLOR_THEME) if hasattr(config, "BOT_LOG_FILE") and config.BOT_LOG_FILE: hdlr = logging.FileHandler(config.BOT_LOG_FILE) hdlr.setFormatter( logging.Formatter("%(asctime)s %(levelname)-8s %(name)-25s %(message)s") ) logger.addHandler(hdlr) if hasattr(config, "BOT_LOG_SENTRY") and config.BOT_LOG_SENTRY: sentry_integrations = [] try: import sentry_sdk from sentry_sdk.integrations.logging import LoggingIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk? " "(See https://docs.sentry.io/platforms/python for installation instructions)" ) exit(-1) sentry_logging = LoggingIntegration( level=config.SENTRY_LOGLEVEL, event_level=config.SENTRY_EVENTLEVEL ) sentry_integrations.append(sentry_logging) if hasattr(config, "BOT_LOG_SENTRY_FLASK") and config.BOT_LOG_SENTRY_FLASK: try: from sentry_sdk.integrations.flask import FlaskIntegration except ImportError: log.exception( "You have BOT_LOG_SENTRY enabled, but I couldn't import modules " "needed for Sentry integration. Did you install sentry-sdk[flask]? " "(See https://docs.sentry.io/platforms/python/flask for installation instructions)" ) exit(-1) sentry_integrations.append(FlaskIntegration()) sentry_options = getattr(config, "SENTRY_OPTIONS", {}) if hasattr(config, "SENTRY_TRANSPORT") and isinstance( config.SENTRY_TRANSPORT, tuple ): warnings.warn( "SENTRY_TRANSPORT is deprecated. Please use SENTRY_OPTIONS instead.", DeprecationWarning, ) try: mod = importlib.import_module(config.SENTRY_TRANSPORT[1]) transport = getattr(mod, config.SENTRY_TRANSPORT[0]) sentry_options["transport"] = transport except ImportError: log.exception( f"Unable to import selected SENTRY_TRANSPORT - {config.SENTRY_TRANSPORT}" ) exit(-1) # merge options dict with dedicated SENTRY_DSN setting sentry_kwargs = { **sentry_options, **{"dsn": config.SENTRY_DSN, "integrations": sentry_integrations}, } sentry_sdk.init(**sentry_kwargs) logger.setLevel(config.BOT_LOG_LEVEL) storage_plugin = get_storage_plugin(config) # init the botplugin manager botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR) if not path.exists(botplugins_dir): makedirs(botplugins_dir, mode=0o755) plugin_indexes = getattr(config, "BOT_PLUGIN_INDEXES", (PLUGIN_DEFAULT_INDEX,)) if isinstance(plugin_indexes, str): plugin_indexes = (plugin_indexes,) # Extra backend is expected to be a list type, convert string to list. extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] backendpm = BackendPluginManager( config, "errbot.backends", backend_name, ErrBot, CORE_BACKENDS, extra_backend ) log.info(f"Found Backend plugin: {backendpm.plugin_info.name}") repo_manager = BotRepoManager(storage_plugin, botplugins_dir, plugin_indexes) try: bot = backendpm.load_plugin() botpm = BotPluginManager( storage_plugin, config.BOT_EXTRA_PLUGIN_DIR, config.AUTOINSTALL_DEPS, getattr(config, "CORE_PLUGINS", None), lambda name, clazz: clazz(bot, name), getattr(config, "PLUGINS_CALLBACK_ORDER", (None,)), ) bot.attach_storage_plugin(storage_plugin) bot.attach_repo_manager(repo_manager) bot.attach_plugin_manager(botpm) bot.initialize_backend_storage() # restore the bot from the restore script if restore: # Prepare the context for the restore script if "repos" in bot: log.fatal("You cannot restore onto a non empty bot.") sys.exit(-1) log.info(f"**** RESTORING the bot from {restore}") restore_bot_from_backup(restore, bot=bot, log=log) print("Restore complete. You can restart the bot normally") sys.exit(0) errors = bot.plugin_manager.update_plugin_places( repo_manager.get_all_repos_paths() ) if errors: startup_errors = "\n".join(errors.values()) log.error("Some plugins failed to load:\n%s", startup_errors) bot._plugin_errors_during_startup = startup_errors return bot except Exception: log.exception("Unable to load or configure the backend.") > exit(-1) E SystemExit: -1 ../../../errbot/bootstrap.py:221: SystemExit ---------------------------- Captured stdout setup ----------------------------- INFO errbot.bootstrap Found Storage plugin: Memory. INFO errbot.bootstrap Found Backend plugin: Test DEBUG errbot.storage Opening storage 'repomgr' DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:26:14,024 INFO errbot.bootstrap Found Storage plugin: Memory. 2024-04-22 07:26:14,037 INFO errbot.bootstrap Found Backend plugin: Test 2024-04-22 07:26:14,038 DEBUG errbot.storage Opening storage 'repomgr' 2024-04-22 07:26:14,051 DEBUG errbot.core ErrBot init. 2024-04-22 07:26:14,052 DEBUG errbot.backends.base Backend init. 2024-04-22 07:26:14,054 ERROR errbot.bootstrap Unable to load or configure the backend. Traceback (most recent call last): File "/build/reproducible-path/errbot-6.2.0+ds/errbot/bootstrap.py", line 186, in setup_bot bot = backendpm.load_plugin() ^^^^^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backend_plugin_manager.py", line 72, in load_plugin return clazz(self._config) ^^^^^^^^^^^^^^^^^^^ File "/build/reproducible-path/errbot-6.2.0+ds/errbot/backends/test.py", line 273, in __init__ super().__init__(config) File "/build/reproducible-path/errbot-6.2.0+ds/errbot/core.py", line 53, in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 930, in __init__ Pool.__init__(self, processes, initializer, initargs) File "/usr/lib/python3.11/multiprocessing/pool.py", line 215, in __init__ self._repopulate_pool() File "/usr/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/multiprocessing/pool.py", line 329, in _repopulate_pool_static w.start() File "/usr/lib/python3.11/multiprocessing/dummy/__init__.py", line 51, in start threading.Thread.start(self) File "/usr/lib/python3.11/threading.py", line 964, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread ____________________ ERROR at setup of test_change_presence ____________________ @pytest.fixture def text_backend(): tempdir = mkdtemp() # reset the config every time sys.modules.pop("errbot.config-template", None) __import__("errbot.config-template") config = sys.modules["errbot.config-template"] bot_config_defaults(config) config.BOT_DATA_DIR = tempdir config.BOT_LOG_FILE = os.path.join(tempdir, "log.txt") config.BOT_EXTRA_PLUGIN_DIR = [] config.BOT_LOG_LEVEL = logging.DEBUG config.BOT_PREFIX = "!" config.BOT_IDENTITY["username"] = "@testme" config.BOT_ADMINS = ["@test_admin"] > return TextBackend(config) tests/backend_tests/text_test.py:29: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/backends/text.py:211: in __init__ super().__init__(config) ../../../errbot/core.py:53: in __init__ self.thread_pool = ThreadPool(bot_config.BOT_ASYNC_POOLSIZE) /usr/lib/python3.11/multiprocessing/pool.py:930: in __init__ Pool.__init__(self, processes, initializer, initargs) /usr/lib/python3.11/multiprocessing/pool.py:215: in __init__ self._repopulate_pool() /usr/lib/python3.11/multiprocessing/pool.py:306: in _repopulate_pool return self._repopulate_pool_static(self._ctx, self.Process, /usr/lib/python3.11/multiprocessing/pool.py:329: in _repopulate_pool_static w.start() /usr/lib/python3.11/multiprocessing/dummy/__init__.py:51: in start threading.Thread.start(self) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: RuntimeError ---------------------------- Captured stdout setup ----------------------------- DEBUG errbot.core ErrBot init. DEBUG errbot.backends.base Backend init. ---------------------------- Captured stderr setup ----------------------------- 2024-04-22 07:26:14,834 DEBUG errbot.core ErrBot init. 2024-04-22 07:26:14,835 DEBUG errbot.backends.base Backend init. ------------------------------ Captured log setup ------------------------------ DEBUG errbot.core:core.py:48 ErrBot init. DEBUG errbot.backends.base:base.py:809 Backend init. =================================== FAILURES =================================== ________________________________ test_streaming ________________________________ def test_streaming(): canary = b"this is my test" * 1000 source = Stream(TestPerson("gbin@gootz.net"), BytesIO(canary)) clients = [StreamingClient() for _ in range(50)] > Tee(source, clients).run() tests/streaming_test.py:17: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../errbot/streaming.py:81: in run thread.start() _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def start(self): """Start the thread's activity. It must be called at most once per thread object. It arranges for the object's run() method to be invoked in a separate thread of control. This method will raise a RuntimeError if called more than once on the same thread object. """ if not self._initialized: raise RuntimeError("thread.__init__() not called") if self._started.is_set(): raise RuntimeError("threads can only be started once") with _active_limbo_lock: _limbo[self] = self try: > _start_new_thread(self._bootstrap, ()) E RuntimeError: can't start new thread /usr/lib/python3.11/threading.py:964: 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.11_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_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 ERROR tests/backend_tests/text_test.py::test_change_presence - RuntimeError: ... = 1 failed, 110 passed, 8 deselected, 2 warnings, 96 errors in 104.18s (0:01:44) = E: pybuild pybuild:389: test: plugin distutils failed with: exit code=1: cd /build/reproducible-path/errbot-6.2.0+ds/.pybuild/cpython3_3.11_errbot/build; python3.11 -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-ATbxaXeJ dh_auto_test: error: pybuild --test --test-pytest -i python{version} -p "3.12 3.11" 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/3678 and its subdirectories Mon Apr 22 19:26:44 UTC 2024 W: No second build log, what happened?