Running as unit: rb-build-arm64_6-80176.service ==================================================================================== Fri Feb 21 23:04:49 UTC 2025 - running /srv/jenkins/bin/reproducible_build.sh (for job reproducible_builder_arm64_6) on jenkins, called using "codethink03-arm64 codethink04-arm64" as arguments. Fri Feb 21 23:04:49 UTC 2025 - actually running "reproducible_build.sh" (md5sum 68e686e434c9ab7bc3ec047d8b309cbc) as "/tmp/jenkins-script-k5FKiHEp" $ git clone https://salsa.debian.org/qa/jenkins.debian.net.git ; more CONTRIBUTING Fri Feb 21 23:04:49 UTC 2025 - checking /var/lib/jenkins/offline_nodes if codethink03-arm64.debian.net is marked as down. Fri Feb 21 23:04:49 UTC 2025 - checking via ssh if codethink03-arm64.debian.net is up. removed '/tmp/read-only-fs-test-n9O1FY' Fri Feb 21 23:04:50 UTC 2025 - checking /var/lib/jenkins/offline_nodes if codethink04-arm64.debian.net is marked as down. Fri Feb 21 23:04:50 UTC 2025 - checking via ssh if codethink04-arm64.debian.net is up. removed '/tmp/read-only-fs-test-MwZ1dj' ok, let's check if forecast-solar is building anywhere yet… ok, forecast-solar is not building anywhere… UPDATE 1 ============================================================================= Initialising reproducibly build of forecast-solar in unstable on arm64 on jenkins now. 1st build will be done on codethink03-arm64.debian.net. 2nd build will be done on codethink04-arm64.debian.net. ============================================================================= Fri Feb 21 23:05:01 UTC 2025 I: starting to build forecast-solar/unstable/arm64 on jenkins on '2025-02-21 23:04' Fri Feb 21 23:05:01 UTC 2025 I: The jenkins build log is/was available at https://jenkins.debian.net/userContent/reproducible/debian/build_service/arm64_6/80176/console.log 1740179101 arm64 unstable forecast-solar Fri Feb 21 23:05:01 UTC 2025 I: Downloading source for unstable/forecast-solar=4.0.0-1 --2025-02-21 23:05:01-- http://deb.debian.org/debian/pool/main/f/forecast-solar/forecast-solar_4.0.0-1.dsc Connecting to 46.16.76.132:3128... connected. Proxy request sent, awaiting response... 200 OK Length: 2518 (2.5K) [text/prs.lines.tag] Saving to: ‘forecast-solar_4.0.0-1.dsc’ 0K .. 100% 401M=0s 2025-02-21 23:05:01 (401 MB/s) - ‘forecast-solar_4.0.0-1.dsc’ saved [2518/2518] --2025-02-21 23:05:01-- http://deb.debian.org/debian/pool/main/f/forecast-solar/forecast-solar_4.0.0-1.dsc Connecting to 46.16.76.132:3128... connected. Proxy request sent, awaiting response... 200 OK Length: 2518 (2.5K) [text/prs.lines.tag] Saving to: ‘forecast-solar_4.0.0-1.dsc’ 0K .. 100% 401M=0s 2025-02-21 23:05:01 (401 MB/s) - ‘forecast-solar_4.0.0-1.dsc’ saved [2518/2518] Fri Feb 21 23:05:01 UTC 2025 I: forecast-solar_4.0.0-1.dsc -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 Format: 3.0 (quilt) Source: forecast-solar Binary: python3-forecast-solar Architecture: all Version: 4.0.0-1 Maintainer: Home Assistant Team Uploaders: Edward Betts , Homepage: https://github.com/klaasnicolaas/forecast_solar Standards-Version: 4.7.0 Vcs-Browser: https://salsa.debian.org/homeassistant-team/deps/forecast-solar Vcs-Git: https://salsa.debian.org/homeassistant-team/deps/forecast-solar.git Testsuite: autopkgtest Testsuite-Triggers: python3-all, python3-aresponses, python3-pytest, python3-pytest-asyncio, python3-pytest-freezer, python3-syrupy Build-Depends: debhelper-compat (= 13), dh-sequence-python3, pybuild-plugin-pyproject, python3-all, python3-poetry-core Build-Depends-Indep: python3-aiodns , python3-aiohttp , python3-aresponses , python3-pytest , python3-pytest-asyncio , python3-pytest-cov , python3-pytest-freezer , python3-syrupy Package-List: python3-forecast-solar deb python optional arch=all Checksums-Sha1: dcd43bf3cd4c13d0b672bb765dbc6da998bf1c69 68579 forecast-solar_4.0.0.orig.tar.gz e426a94a618a4cabdddb1be29adacf6c189d04b4 2692 forecast-solar_4.0.0-1.debian.tar.xz Checksums-Sha256: b5dc1bbd6644c50d9916af6b212e8fd630f09f273ed7828cb942865d43afba5a 68579 forecast-solar_4.0.0.orig.tar.gz 427cbc8f730b074f5e3bb961d4096699b48fa6f41423174cc78f9061f4f54dfa 2692 forecast-solar_4.0.0-1.debian.tar.xz Files: d8f1bbe5a2b588a740455ee4bc8ba2a7 68579 forecast-solar_4.0.0.orig.tar.gz b6e8471bc30d82ac716de1100e1d74c4 2692 forecast-solar_4.0.0-1.debian.tar.xz -----BEGIN PGP SIGNATURE----- iQIzBAEBCgAdFiEE+4rPp4xyYInDitAmlgWhCYxjuSoFAme4croACgkQlgWhCYxj uSrrXRAAsSf3/Y9WVN/WONMeBP9vmCWjO6J2YKWY4jX5379iSQrnauriJ9SB1yOQ SBezZTmSqZjx222AXXn5K4zNf9Ps7RtHrvae9Ogta+a3uOXnhD48gmlwU2pBBDsU kyvlNt2pOS/yAoLjeGXWUd6TMYyeJZOXOhRdmT/VJ/XnpYtKPwzSVRfaO5zDm0c7 RTeMjD6bfo3H8l52TbP69bM63igqe3GC9bRX5cYt2NGNj7S0moB6/nN9yNXnXzaY XyNprGdC9R4LbLgfsBcKwxbsPkbg0irqIpkG9v0gxCmLYK4gNVGVkc8owsn9h7aW MPvkNsYGzv6vSKe3/Em/EYNwxLV7jy9rfZfUDnzi6daOpqq54C8Bu6EBAPNPQJOa CqD3cyqSHC+bdswZiIMCssxg85hptHEnptmcC/CRchijGzViZJ09ecepHzI6dF8B 7Ea8aVcnFHdearAaYhw0sGMW5NSq2Gg2CnEpCMsVSPoHHQ47gwtjiLhiWE/cvguK byVjXR2zW6gt4x8CBxwcmzgE/qWyOTfAkEGyOri6cJnHT+1n37YFAjVoLSpoFUDF nO9R6EnBN+AMYFWhogvwftOHlyrCIjbmmxcEHXpIuTsdqrmyIZiFRni6jhDeow4D zrdnXYw/VCFozz2aTtej3cAh/QTkTDZdbPFUbUjoT+Z8ccguKZ4= =L68J -----END PGP SIGNATURE----- Fri Feb 21 23:05:01 UTC 2025 I: Checking whether the package is not for us Fri Feb 21 23:05:01 UTC 2025 I: Starting 1st build on remote node codethink03-arm64.debian.net. Fri Feb 21 23:05:01 UTC 2025 I: Preparing to do remote build '1' on codethink03-arm64.debian.net. Fri Feb 21 23:05:01 UTC 2025 - checking /var/lib/jenkins/offline_nodes if codethink03-arm64.debian.net is marked as down. Fri Feb 21 23:05:01 UTC 2025 - checking via ssh if codethink03-arm64.debian.net is up. removed '/tmp/read-only-fs-test-qxctzV' ==================================================================================== Fri Mar 27 05:28:02 UTC 2026 - running /srv/jenkins/bin/reproducible_build.sh (for job /srv/jenkins/bin/reproducible_build.sh) on codethink03-arm64, called using "1 forecast-solar unstable /srv/reproducible-results/rbuild-debian/r-b-build.65nWBFE2 4.0.0-1" as arguments. Fri Mar 27 05:28:02 UTC 2026 - actually running "reproducible_build.sh" (md5sum 68e686e434c9ab7bc3ec047d8b309cbc) as "/tmp/jenkins-script-RlLliAYv" $ git clone https://salsa.debian.org/qa/jenkins.debian.net.git ; more CONTRIBUTING Fri Mar 27 05:28:03 UTC 2026 I: Downloading source for unstable/forecast-solar=4.0.0-1 Reading package lists... NOTICE: 'forecast-solar' packaging is maintained in the 'Git' version control system at: https://salsa.debian.org/homeassistant-team/deps/forecast-solar.git Please use: git clone https://salsa.debian.org/homeassistant-team/deps/forecast-solar.git to retrieve the latest (possibly unreleased) updates to the package. Need to get 73.8 kB of source archives. Get:1 http://deb.debian.org/debian unstable/main forecast-solar 4.0.0-1 (dsc) [2518 B] Get:2 http://deb.debian.org/debian unstable/main forecast-solar 4.0.0-1 (tar) [68.6 kB] Get:3 http://deb.debian.org/debian unstable/main forecast-solar 4.0.0-1 (diff) [2692 B] Fetched 73.8 kB in 0s (5810 kB/s) Download complete and in download only mode Reading package lists... NOTICE: 'forecast-solar' packaging is maintained in the 'Git' version control system at: https://salsa.debian.org/homeassistant-team/deps/forecast-solar.git Please use: git clone https://salsa.debian.org/homeassistant-team/deps/forecast-solar.git to retrieve the latest (possibly unreleased) updates to the package. Need to get 73.8 kB of source archives. Get:1 http://deb.debian.org/debian unstable/main forecast-solar 4.0.0-1 (dsc) [2518 B] Get:2 http://deb.debian.org/debian unstable/main forecast-solar 4.0.0-1 (tar) [68.6 kB] Get:3 http://deb.debian.org/debian unstable/main forecast-solar 4.0.0-1 (diff) [2692 B] Fetched 73.8 kB in 0s (5810 kB/s) Download complete and in download only mode ============================================================================= Building forecast-solar in unstable on arm64 on codethink03-arm64 now. Date: Fri Mar 27 05:28:03 GMT 2026 Date UTC: Fri Mar 27 05:28:03 UTC 2026 ============================================================================= W: /root/.pbuilderrc does not exist I: Logging to b1/build.log I: pbuilder: network access will be disabled during build I: Current time: Thu Mar 26 17:28:03 -12 2026 I: pbuilder-time-stamp: 1774589283 I: Building the build Environment I: extracting base tarball [/var/cache/pbuilder/unstable-reproducible-base.tgz] I: copying local configuration W: --override-config is not set; not updating apt.conf Read the manpage for details. I: mounting /proc filesystem I: mounting /sys filesystem I: creating /{dev,run}/shm I: mounting /dev/pts filesystem I: redirecting /dev/ptmx to /dev/pts/ptmx I: policy-rc.d already exists I: Copying source file I: copying [forecast-solar_4.0.0-1.dsc] I: copying [./forecast-solar_4.0.0.orig.tar.gz] I: copying [./forecast-solar_4.0.0-1.debian.tar.xz] I: Extracting source dpkg-source: warning: cannot verify inline signature for ./forecast-solar_4.0.0-1.dsc: unsupported subcommand dpkg-source: info: extracting forecast-solar in forecast-solar-4.0.0 dpkg-source: info: unpacking forecast-solar_4.0.0.orig.tar.gz dpkg-source: info: unpacking forecast-solar_4.0.0-1.debian.tar.xz I: Not using root during the build. I: Installing the build-deps I: user script /srv/workspace/pbuilder/395853/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='arm64' DEBIAN_FRONTEND='noninteractive' DEB_BUILD_OPTIONS='buildinfo=+all reproducible=+all parallel=12 ' DISTRIBUTION='unstable' HOME='/root' HOST_ARCH='arm64' IFS=' ' INVOCATION_ID='e5b845bcbfa04ba5a31c60de24f672d0' 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='395853' 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.65nWBFE2/pbuilderrc_oT0O --distribution unstable --hookdir /etc/pbuilder/first-build-hooks --debbuildopts -b --basetgz /var/cache/pbuilder/unstable-reproducible-base.tgz --buildresult /srv/reproducible-results/rbuild-debian/r-b-build.65nWBFE2/b1 --logfile b1/build.log forecast-solar_4.0.0-1.dsc' SUDO_GID='109' SUDO_UID='104' SUDO_USER='jenkins' TERM='unknown' TZ='/usr/share/zoneinfo/Etc/GMT+12' USER='root' _='/usr/bin/systemd-run' http_proxy='http://192.168.101.4:3128' I: uname -a Linux codethink03-arm64 6.1.0-31-cloud-arm64 #1 SMP Debian 6.1.128-1 (2025-02-07) aarch64 GNU/Linux I: ls -l /bin lrwxrwxrwx 1 root root 7 Nov 22 2024 /bin -> usr/bin I: user script /srv/workspace/pbuilder/395853/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: arm64 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, pybuild-plugin-pyproject, python3-all, python3-poetry-core, python3-aiodns, python3-aiohttp, python3-aresponses, python3-pytest, python3-pytest-asyncio, python3-pytest-cov, python3-pytest-freezer, python3-syrupy dpkg-deb: building package 'pbuilder-satisfydepends-dummy' in '/tmp/satisfydepends-aptitude/pbuilder-satisfydepends-dummy.deb'. Selecting previously unselected package pbuilder-satisfydepends-dummy. (Reading database ... 19889 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 pybuild-plugin-pyproject; however: Package pybuild-plugin-pyproject is not installed. pbuilder-satisfydepends-dummy depends on python3-all; however: Package python3-all is not installed. pbuilder-satisfydepends-dummy depends on python3-poetry-core; however: Package python3-poetry-core is not installed. pbuilder-satisfydepends-dummy depends on python3-aiodns; however: Package python3-aiodns is not installed. pbuilder-satisfydepends-dummy depends on python3-aiohttp; however: Package python3-aiohttp is not installed. pbuilder-satisfydepends-dummy depends on python3-aresponses; however: Package python3-aresponses is not installed. pbuilder-satisfydepends-dummy depends on python3-pytest; however: Package python3-pytest is not installed. pbuilder-satisfydepends-dummy depends on python3-pytest-asyncio; however: Package python3-pytest-asyncio is not installed. pbuilder-satisfydepends-dummy depends on python3-pytest-cov; however: Package python3-pytest-cov is not installed. pbuilder-satisfydepends-dummy depends on python3-pytest-freezer; however: Package python3-pytest-freezer is not installed. pbuilder-satisfydepends-dummy depends on python3-syrupy; however: Package python3-syrupy 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} debhelper{a} dh-autoreconf{a} dh-python{a} dh-strip-nondeterminism{a} dwz{a} file{a} gettext{a} gettext-base{a} groff-base{a} intltool-debian{a} libarchive-zip-perl{a} libcares2{a} libcom-err2{a} libdebhelper-perl{a} libelf1t64{a} libexpat1{a} libffi8{a} libfile-stripnondeterminism-perl{a} libgssapi-krb5-2{a} libicu72{a} libjs-jquery{a} libjs-jquery-hotkeys{a} libjs-jquery-isonscreen{a} libjs-jquery-metadata{a} libjs-jquery-tablesorter{a} libjs-jquery-throttle-debounce{a} libk5crypto3{a} libkeyutils1{a} libkrb5-3{a} libkrb5support0{a} libmagic-mgc{a} libmagic1t64{a} libnsl2{a} libpipeline1{a} libpython3-stdlib{a} libpython3.12-minimal{a} libpython3.12-stdlib{a} libpython3.13-minimal{a} libpython3.13-stdlib{a} libreadline8t64{a} libtirpc-common{a} libtirpc3t64{a} libtool{a} libuchardet0{a} libunistring5{a} libxml2{a} m4{a} man-db{a} media-types{a} netbase{a} po-debconf{a} pybuild-plugin-pyproject{a} python3{a} python3-aiodns{a} python3-aiohappyeyeballs{a} python3-aiohttp{a} python3-aiosignal{a} python3-all{a} python3-aresponses{a} python3-async-generator{a} python3-async-timeout{a} python3-attr{a} python3-autocommand{a} python3-build{a} python3-cffi-backend{a} python3-coverage{a} python3-dateutil{a} python3-freezegun{a} python3-frozenlist{a} python3-idna{a} python3-inflect{a} python3-iniconfig{a} python3-installer{a} python3-jaraco.context{a} python3-jaraco.functools{a} python3-jaraco.text{a} python3-minimal{a} python3-more-itertools{a} python3-multidict{a} python3-packaging{a} python3-pkg-resources{a} python3-pluggy{a} python3-poetry-core{a} python3-pycares{a} python3-pyproject-hooks{a} python3-pytest{a} python3-pytest-asyncio{a} python3-pytest-cov{a} python3-pytest-freezer{a} python3-setuptools{a} python3-syrupy{a} python3-toml{a} python3-typeguard{a} python3-typing-extensions{a} python3-wheel{a} python3-yarl{a} python3-zipp{a} python3.12{a} python3.12-minimal{a} python3.13{a} python3.13-minimal{a} readline-common{a} sensible-utils{a} tzdata{a} The following packages are RECOMMENDED but will NOT be installed: ca-certificates curl javascript-common krb5-locales libarchive-cpio-perl libltdl-dev libmail-sendmail-perl lynx python3-pygments wget 0 packages upgraded, 109 newly installed, 0 to remove and 0 not upgraded. Need to get 36.3 MB of archives. After unpacking 154 MB will be used. Writing extended state information... Get: 1 http://deb.debian.org/debian unstable/main arm64 libpython3.13-minimal arm64 3.13.2-1 [853 kB] Get: 2 http://deb.debian.org/debian unstable/main arm64 libexpat1 arm64 2.6.4-1 [90.7 kB] Get: 3 http://deb.debian.org/debian unstable/main arm64 python3.13-minimal arm64 3.13.2-1 [1997 kB] Get: 4 http://deb.debian.org/debian unstable/main arm64 python3-minimal arm64 3.13.1-2 [27.0 kB] Get: 5 http://deb.debian.org/debian unstable/main arm64 media-types all 11.0.0 [27.6 kB] Get: 6 http://deb.debian.org/debian unstable/main arm64 netbase all 6.4 [12.8 kB] Get: 7 http://deb.debian.org/debian unstable/main arm64 tzdata all 2025a-2 [259 kB] Get: 8 http://deb.debian.org/debian unstable/main arm64 libffi8 arm64 3.4.7-1 [21.2 kB] Get: 9 http://deb.debian.org/debian unstable/main arm64 readline-common all 8.2-6 [69.4 kB] Get: 10 http://deb.debian.org/debian unstable/main arm64 libreadline8t64 arm64 8.2-6 [159 kB] Get: 11 http://deb.debian.org/debian unstable/main arm64 libpython3.13-stdlib arm64 3.13.2-1 [1914 kB] Get: 12 http://deb.debian.org/debian unstable/main arm64 python3.13 arm64 3.13.2-1 [745 kB] Get: 13 http://deb.debian.org/debian unstable/main arm64 libpython3-stdlib arm64 3.13.1-2 [9952 B] Get: 14 http://deb.debian.org/debian unstable/main arm64 python3 arm64 3.13.1-2 [28.0 kB] Get: 15 http://deb.debian.org/debian unstable/main arm64 libpython3.12-minimal arm64 3.12.9-1 [810 kB] Get: 16 http://deb.debian.org/debian unstable/main arm64 python3.12-minimal arm64 3.12.9-1 [1941 kB] Get: 17 http://deb.debian.org/debian unstable/main arm64 sensible-utils all 0.0.24 [24.8 kB] Get: 18 http://deb.debian.org/debian unstable/main arm64 libmagic-mgc arm64 1:5.45-3+b1 [314 kB] Get: 19 http://deb.debian.org/debian unstable/main arm64 libmagic1t64 arm64 1:5.45-3+b1 [102 kB] Get: 20 http://deb.debian.org/debian unstable/main arm64 file arm64 1:5.45-3+b1 [43.4 kB] Get: 21 http://deb.debian.org/debian unstable/main arm64 gettext-base arm64 0.23.1-1 [241 kB] Get: 22 http://deb.debian.org/debian unstable/main arm64 libuchardet0 arm64 0.0.8-1+b2 [69.2 kB] Get: 23 http://deb.debian.org/debian unstable/main arm64 groff-base arm64 1.23.0-7 [1129 kB] Get: 24 http://deb.debian.org/debian unstable/main arm64 bsdextrautils arm64 2.40.4-4 [92.1 kB] Get: 25 http://deb.debian.org/debian unstable/main arm64 libpipeline1 arm64 1.5.8-1 [40.2 kB] Get: 26 http://deb.debian.org/debian unstable/main arm64 man-db arm64 2.13.0-1 [1404 kB] Get: 27 http://deb.debian.org/debian unstable/main arm64 m4 arm64 1.4.19-5 [284 kB] Get: 28 http://deb.debian.org/debian unstable/main arm64 autoconf all 2.72-3 [493 kB] Get: 29 http://deb.debian.org/debian unstable/main arm64 autotools-dev all 20220109.1 [51.6 kB] Get: 30 http://deb.debian.org/debian unstable/main arm64 automake all 1:1.17-3 [862 kB] Get: 31 http://deb.debian.org/debian unstable/main arm64 autopoint all 0.23.1-1 [770 kB] Get: 32 http://deb.debian.org/debian unstable/main arm64 libdebhelper-perl all 13.24.1 [90.9 kB] Get: 33 http://deb.debian.org/debian unstable/main arm64 libtool all 2.5.4-3 [539 kB] Get: 34 http://deb.debian.org/debian unstable/main arm64 dh-autoreconf all 20 [17.1 kB] Get: 35 http://deb.debian.org/debian unstable/main arm64 libarchive-zip-perl all 1.68-1 [104 kB] Get: 36 http://deb.debian.org/debian unstable/main arm64 libfile-stripnondeterminism-perl all 1.14.1-2 [19.7 kB] Get: 37 http://deb.debian.org/debian unstable/main arm64 dh-strip-nondeterminism all 1.14.1-2 [8620 B] Get: 38 http://deb.debian.org/debian unstable/main arm64 libelf1t64 arm64 0.192-4 [189 kB] Get: 39 http://deb.debian.org/debian unstable/main arm64 dwz arm64 0.15-1+b1 [102 kB] Get: 40 http://deb.debian.org/debian unstable/main arm64 libunistring5 arm64 1.3-1 [449 kB] Get: 41 http://deb.debian.org/debian unstable/main arm64 libicu72 arm64 72.1-6 [9239 kB] Get: 42 http://deb.debian.org/debian unstable/main arm64 libxml2 arm64 2.12.7+dfsg+really2.9.14-0.2+b1 [630 kB] Get: 43 http://deb.debian.org/debian unstable/main arm64 gettext arm64 0.23.1-1 [1610 kB] Get: 44 http://deb.debian.org/debian unstable/main arm64 intltool-debian all 0.35.0+20060710.6 [22.9 kB] Get: 45 http://deb.debian.org/debian unstable/main arm64 po-debconf all 1.0.21+nmu1 [248 kB] Get: 46 http://deb.debian.org/debian unstable/main arm64 debhelper all 13.24.1 [920 kB] Get: 47 http://deb.debian.org/debian unstable/main arm64 python3-autocommand all 2.2.2-3 [13.6 kB] Get: 48 http://deb.debian.org/debian unstable/main arm64 python3-more-itertools all 10.6.0-1 [65.3 kB] Get: 49 http://deb.debian.org/debian unstable/main arm64 python3-typing-extensions all 4.12.2-2 [73.0 kB] Get: 50 http://deb.debian.org/debian unstable/main arm64 python3-typeguard all 4.4.1-1 [37.0 kB] Get: 51 http://deb.debian.org/debian unstable/main arm64 python3-inflect all 7.3.1-2 [32.4 kB] Get: 52 http://deb.debian.org/debian unstable/main arm64 python3-jaraco.context all 6.0.0-1 [7984 B] Get: 53 http://deb.debian.org/debian unstable/main arm64 python3-jaraco.functools all 4.1.0-1 [12.0 kB] Get: 54 http://deb.debian.org/debian unstable/main arm64 python3-pkg-resources all 75.8.0-1 [222 kB] Get: 55 http://deb.debian.org/debian unstable/main arm64 python3-jaraco.text all 4.0.0-1 [11.4 kB] Get: 56 http://deb.debian.org/debian unstable/main arm64 python3-zipp all 3.21.0-1 [10.6 kB] Get: 57 http://deb.debian.org/debian unstable/main arm64 python3-setuptools all 75.8.0-1 [724 kB] Get: 58 http://deb.debian.org/debian unstable/main arm64 dh-python all 6.20250108 [113 kB] Get: 59 http://deb.debian.org/debian unstable/main arm64 libcares2 arm64 1.34.4-2.1 [92.2 kB] Get: 60 http://deb.debian.org/debian unstable/main arm64 libcom-err2 arm64 1.47.2-1 [23.9 kB] Get: 61 http://deb.debian.org/debian unstable/main arm64 libkrb5support0 arm64 1.21.3-4 [32.2 kB] Get: 62 http://deb.debian.org/debian unstable/main arm64 libk5crypto3 arm64 1.21.3-4 [81.5 kB] Get: 63 http://deb.debian.org/debian unstable/main arm64 libkeyutils1 arm64 1.6.3-4 [9352 B] Get: 64 http://deb.debian.org/debian unstable/main arm64 libkrb5-3 arm64 1.21.3-4 [308 kB] Get: 65 http://deb.debian.org/debian unstable/main arm64 libgssapi-krb5-2 arm64 1.21.3-4 [127 kB] Get: 66 http://deb.debian.org/debian unstable/main arm64 libjs-jquery all 3.6.1+dfsg+~3.5.14-1 [326 kB] Get: 67 http://deb.debian.org/debian unstable/main arm64 libjs-jquery-hotkeys all 0.2.0-1 [12.6 kB] Get: 68 http://deb.debian.org/debian unstable/main arm64 libjs-jquery-isonscreen all 1.2.0-1.1 [3196 B] Get: 69 http://deb.debian.org/debian unstable/main arm64 libjs-jquery-metadata all 12-4 [6532 B] Get: 70 http://deb.debian.org/debian unstable/main arm64 libjs-jquery-tablesorter all 1:2.31.3+dfsg1-4 [184 kB] Get: 71 http://deb.debian.org/debian unstable/main arm64 libjs-jquery-throttle-debounce all 1.1+dfsg.1-2 [12.2 kB] Get: 72 http://deb.debian.org/debian unstable/main arm64 libtirpc-common all 1.3.4+ds-1.3 [10.9 kB] Get: 73 http://deb.debian.org/debian unstable/main arm64 libtirpc3t64 arm64 1.3.4+ds-1.3+b1 [78.7 kB] Get: 74 http://deb.debian.org/debian unstable/main arm64 libnsl2 arm64 1.3.0-3+b3 [37.9 kB] Get: 75 http://deb.debian.org/debian unstable/main arm64 libpython3.12-stdlib arm64 3.12.9-1 [1909 kB] Get: 76 http://deb.debian.org/debian unstable/main arm64 python3-packaging all 24.2-1 [55.3 kB] Get: 77 http://deb.debian.org/debian unstable/main arm64 python3-pyproject-hooks all 1.2.0-1 [11.7 kB] Get: 78 http://deb.debian.org/debian unstable/main arm64 python3-toml all 0.10.2-1 [16.2 kB] Get: 79 http://deb.debian.org/debian unstable/main arm64 python3-wheel all 0.45.1-1 [56.7 kB] Get: 80 http://deb.debian.org/debian unstable/main arm64 python3-build all 1.2.2-1 [36.0 kB] Get: 81 http://deb.debian.org/debian unstable/main arm64 python3-installer all 0.7.0+dfsg1-3 [18.6 kB] Get: 82 http://deb.debian.org/debian unstable/main arm64 pybuild-plugin-pyproject all 6.20250108 [11.6 kB] Get: 83 http://deb.debian.org/debian unstable/main arm64 python3-idna all 3.10-1 [42.0 kB] Get: 84 http://deb.debian.org/debian unstable/main arm64 python3-cffi-backend arm64 1.17.1-2+b1 [94.8 kB] Get: 85 http://deb.debian.org/debian unstable/main arm64 python3-pycares arm64 4.4.0-2+b3 [30.6 kB] Get: 86 http://deb.debian.org/debian unstable/main arm64 python3-aiodns all 3.2.0-2 [8216 B] Get: 87 http://deb.debian.org/debian unstable/main arm64 python3-aiohappyeyeballs all 2.4.6-1 [13.5 kB] Get: 88 http://deb.debian.org/debian unstable/main arm64 python3-multidict arm64 6.1.0-1+b1 [37.8 kB] Get: 89 http://deb.debian.org/debian unstable/main arm64 python3-yarl arm64 1.13.1-1+b1 [106 kB] Get: 90 http://deb.debian.org/debian unstable/main arm64 python3-async-timeout all 5.0.1-1 [8324 B] Get: 91 http://deb.debian.org/debian unstable/main arm64 python3-frozenlist arm64 1.5.0-1+b2 [53.8 kB] Get: 92 http://deb.debian.org/debian unstable/main arm64 python3-aiosignal all 1.3.2-1 [6100 B] Get: 93 http://deb.debian.org/debian unstable/main arm64 python3-attr all 25.1.0-1 [68.7 kB] Get: 94 http://deb.debian.org/debian unstable/main arm64 python3-aiohttp arm64 3.10.11-1 [335 kB] Get: 95 http://deb.debian.org/debian unstable/main arm64 python3.12 arm64 3.12.9-1 [681 kB] Get: 96 http://deb.debian.org/debian unstable/main arm64 python3-all arm64 3.13.1-2 [1056 B] Get: 97 http://deb.debian.org/debian unstable/main arm64 python3-async-generator all 1.10-4 [17.4 kB] Get: 98 http://deb.debian.org/debian unstable/main arm64 python3-iniconfig all 1.1.1-2 [6396 B] Get: 99 http://deb.debian.org/debian unstable/main arm64 python3-pluggy all 1.5.0-1 [26.9 kB] Get: 100 http://deb.debian.org/debian unstable/main arm64 python3-pytest all 8.3.4-1 [250 kB] Get: 101 http://deb.debian.org/debian unstable/main arm64 python3-pytest-asyncio all 0.25.1-1 [16.7 kB] Get: 102 http://deb.debian.org/debian unstable/main arm64 python3-aresponses all 3.0.0-2 [13.5 kB] Get: 103 http://deb.debian.org/debian unstable/main arm64 python3-coverage arm64 7.6.0+dfsg1-2+b1 [177 kB] Get: 104 http://deb.debian.org/debian unstable/main arm64 python3-dateutil all 2.9.0-3 [79.3 kB] Get: 105 http://deb.debian.org/debian unstable/main arm64 python3-freezegun all 1.5.1-1.2 [17.2 kB] Get: 106 http://deb.debian.org/debian unstable/main arm64 python3-poetry-core all 2.0.1-2 [225 kB] Get: 107 http://deb.debian.org/debian unstable/main arm64 python3-pytest-cov all 5.0.0-1 [26.8 kB] Get: 108 http://deb.debian.org/debian unstable/main arm64 python3-pytest-freezer all 0.4.9-1 [4544 B] Get: 109 http://deb.debian.org/debian unstable/main arm64 python3-syrupy all 4.8.1-1 [54.2 kB] Fetched 36.3 MB in 0s (75.6 MB/s) Preconfiguring packages ... Selecting previously unselected package libpython3.13-minimal:arm64. (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 ... 19889 files and directories currently installed.) Preparing to unpack .../libpython3.13-minimal_3.13.2-1_arm64.deb ... Unpacking libpython3.13-minimal:arm64 (3.13.2-1) ... Selecting previously unselected package libexpat1:arm64. Preparing to unpack .../libexpat1_2.6.4-1_arm64.deb ... Unpacking libexpat1:arm64 (2.6.4-1) ... Selecting previously unselected package python3.13-minimal. Preparing to unpack .../python3.13-minimal_3.13.2-1_arm64.deb ... Unpacking python3.13-minimal (3.13.2-1) ... Setting up libpython3.13-minimal:arm64 (3.13.2-1) ... Setting up libexpat1:arm64 (2.6.4-1) ... Setting up python3.13-minimal (3.13.2-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 ... 20223 files and directories currently installed.) Preparing to unpack .../0-python3-minimal_3.13.1-2_arm64.deb ... Unpacking python3-minimal (3.13.1-2) ... Selecting previously unselected package media-types. Preparing to unpack .../1-media-types_11.0.0_all.deb ... Unpacking media-types (11.0.0) ... Selecting previously unselected package netbase. Preparing to unpack .../2-netbase_6.4_all.deb ... Unpacking netbase (6.4) ... Selecting previously unselected package tzdata. Preparing to unpack .../3-tzdata_2025a-2_all.deb ... Unpacking tzdata (2025a-2) ... Selecting previously unselected package libffi8:arm64. Preparing to unpack .../4-libffi8_3.4.7-1_arm64.deb ... Unpacking libffi8:arm64 (3.4.7-1) ... Selecting previously unselected package readline-common. Preparing to unpack .../5-readline-common_8.2-6_all.deb ... Unpacking readline-common (8.2-6) ... Selecting previously unselected package libreadline8t64:arm64. Preparing to unpack .../6-libreadline8t64_8.2-6_arm64.deb ... Adding 'diversion of /lib/aarch64-linux-gnu/libhistory.so.8 to /lib/aarch64-linux-gnu/libhistory.so.8.usr-is-merged by libreadline8t64' Adding 'diversion of /lib/aarch64-linux-gnu/libhistory.so.8.2 to /lib/aarch64-linux-gnu/libhistory.so.8.2.usr-is-merged by libreadline8t64' Adding 'diversion of /lib/aarch64-linux-gnu/libreadline.so.8 to /lib/aarch64-linux-gnu/libreadline.so.8.usr-is-merged by libreadline8t64' Adding 'diversion of /lib/aarch64-linux-gnu/libreadline.so.8.2 to /lib/aarch64-linux-gnu/libreadline.so.8.2.usr-is-merged by libreadline8t64' Unpacking libreadline8t64:arm64 (8.2-6) ... Selecting previously unselected package libpython3.13-stdlib:arm64. Preparing to unpack .../7-libpython3.13-stdlib_3.13.2-1_arm64.deb ... Unpacking libpython3.13-stdlib:arm64 (3.13.2-1) ... Selecting previously unselected package python3.13. Preparing to unpack .../8-python3.13_3.13.2-1_arm64.deb ... Unpacking python3.13 (3.13.2-1) ... Selecting previously unselected package libpython3-stdlib:arm64. Preparing to unpack .../9-libpython3-stdlib_3.13.1-2_arm64.deb ... Unpacking libpython3-stdlib:arm64 (3.13.1-2) ... Setting up python3-minimal (3.13.1-2) ... Selecting previously unselected package python3. (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 21233 files and directories currently installed.) Preparing to unpack .../00-python3_3.13.1-2_arm64.deb ... Unpacking python3 (3.13.1-2) ... Selecting previously unselected package libpython3.12-minimal:arm64. Preparing to unpack .../01-libpython3.12-minimal_3.12.9-1_arm64.deb ... Unpacking libpython3.12-minimal:arm64 (3.12.9-1) ... Selecting previously unselected package python3.12-minimal. Preparing to unpack .../02-python3.12-minimal_3.12.9-1_arm64.deb ... Unpacking python3.12-minimal (3.12.9-1) ... Selecting previously unselected package sensible-utils. Preparing to unpack .../03-sensible-utils_0.0.24_all.deb ... Unpacking sensible-utils (0.0.24) ... Selecting previously unselected package libmagic-mgc. Preparing to unpack .../04-libmagic-mgc_1%3a5.45-3+b1_arm64.deb ... Unpacking libmagic-mgc (1:5.45-3+b1) ... Selecting previously unselected package libmagic1t64:arm64. Preparing to unpack .../05-libmagic1t64_1%3a5.45-3+b1_arm64.deb ... Unpacking libmagic1t64:arm64 (1:5.45-3+b1) ... Selecting previously unselected package file. Preparing to unpack .../06-file_1%3a5.45-3+b1_arm64.deb ... Unpacking file (1:5.45-3+b1) ... Selecting previously unselected package gettext-base. Preparing to unpack .../07-gettext-base_0.23.1-1_arm64.deb ... Unpacking gettext-base (0.23.1-1) ... Selecting previously unselected package libuchardet0:arm64. Preparing to unpack .../08-libuchardet0_0.0.8-1+b2_arm64.deb ... Unpacking libuchardet0:arm64 (0.0.8-1+b2) ... Selecting previously unselected package groff-base. Preparing to unpack .../09-groff-base_1.23.0-7_arm64.deb ... Unpacking groff-base (1.23.0-7) ... Selecting previously unselected package bsdextrautils. Preparing to unpack .../10-bsdextrautils_2.40.4-4_arm64.deb ... Unpacking bsdextrautils (2.40.4-4) ... Selecting previously unselected package libpipeline1:arm64. Preparing to unpack .../11-libpipeline1_1.5.8-1_arm64.deb ... Unpacking libpipeline1:arm64 (1.5.8-1) ... Selecting previously unselected package man-db. Preparing to unpack .../12-man-db_2.13.0-1_arm64.deb ... Unpacking man-db (2.13.0-1) ... Selecting previously unselected package m4. Preparing to unpack .../13-m4_1.4.19-5_arm64.deb ... Unpacking m4 (1.4.19-5) ... Selecting previously unselected package autoconf. Preparing to unpack .../14-autoconf_2.72-3_all.deb ... Unpacking autoconf (2.72-3) ... Selecting previously unselected package autotools-dev. Preparing to unpack .../15-autotools-dev_20220109.1_all.deb ... Unpacking autotools-dev (20220109.1) ... Selecting previously unselected package automake. Preparing to unpack .../16-automake_1%3a1.17-3_all.deb ... Unpacking automake (1:1.17-3) ... Selecting previously unselected package autopoint. Preparing to unpack .../17-autopoint_0.23.1-1_all.deb ... Unpacking autopoint (0.23.1-1) ... Selecting previously unselected package libdebhelper-perl. Preparing to unpack .../18-libdebhelper-perl_13.24.1_all.deb ... Unpacking libdebhelper-perl (13.24.1) ... Selecting previously unselected package libtool. Preparing to unpack .../19-libtool_2.5.4-3_all.deb ... Unpacking libtool (2.5.4-3) ... Selecting previously unselected package dh-autoreconf. Preparing to unpack .../20-dh-autoreconf_20_all.deb ... Unpacking dh-autoreconf (20) ... Selecting previously unselected package libarchive-zip-perl. Preparing to unpack .../21-libarchive-zip-perl_1.68-1_all.deb ... Unpacking libarchive-zip-perl (1.68-1) ... Selecting previously unselected package libfile-stripnondeterminism-perl. Preparing to unpack .../22-libfile-stripnondeterminism-perl_1.14.1-2_all.deb ... Unpacking libfile-stripnondeterminism-perl (1.14.1-2) ... Selecting previously unselected package dh-strip-nondeterminism. Preparing to unpack .../23-dh-strip-nondeterminism_1.14.1-2_all.deb ... Unpacking dh-strip-nondeterminism (1.14.1-2) ... Selecting previously unselected package libelf1t64:arm64. Preparing to unpack .../24-libelf1t64_0.192-4_arm64.deb ... Unpacking libelf1t64:arm64 (0.192-4) ... Selecting previously unselected package dwz. Preparing to unpack .../25-dwz_0.15-1+b1_arm64.deb ... Unpacking dwz (0.15-1+b1) ... Selecting previously unselected package libunistring5:arm64. Preparing to unpack .../26-libunistring5_1.3-1_arm64.deb ... Unpacking libunistring5:arm64 (1.3-1) ... Selecting previously unselected package libicu72:arm64. Preparing to unpack .../27-libicu72_72.1-6_arm64.deb ... Unpacking libicu72:arm64 (72.1-6) ... Selecting previously unselected package libxml2:arm64. Preparing to unpack .../28-libxml2_2.12.7+dfsg+really2.9.14-0.2+b1_arm64.deb ... Unpacking libxml2:arm64 (2.12.7+dfsg+really2.9.14-0.2+b1) ... Selecting previously unselected package gettext. Preparing to unpack .../29-gettext_0.23.1-1_arm64.deb ... Unpacking gettext (0.23.1-1) ... Selecting previously unselected package intltool-debian. Preparing to unpack .../30-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 .../31-po-debconf_1.0.21+nmu1_all.deb ... Unpacking po-debconf (1.0.21+nmu1) ... Selecting previously unselected package debhelper. Preparing to unpack .../32-debhelper_13.24.1_all.deb ... Unpacking debhelper (13.24.1) ... Selecting previously unselected package python3-autocommand. Preparing to unpack .../33-python3-autocommand_2.2.2-3_all.deb ... Unpacking python3-autocommand (2.2.2-3) ... Selecting previously unselected package python3-more-itertools. Preparing to unpack .../34-python3-more-itertools_10.6.0-1_all.deb ... Unpacking python3-more-itertools (10.6.0-1) ... Selecting previously unselected package python3-typing-extensions. Preparing to unpack .../35-python3-typing-extensions_4.12.2-2_all.deb ... Unpacking python3-typing-extensions (4.12.2-2) ... Selecting previously unselected package python3-typeguard. Preparing to unpack .../36-python3-typeguard_4.4.1-1_all.deb ... Unpacking python3-typeguard (4.4.1-1) ... Selecting previously unselected package python3-inflect. Preparing to unpack .../37-python3-inflect_7.3.1-2_all.deb ... Unpacking python3-inflect (7.3.1-2) ... Selecting previously unselected package python3-jaraco.context. Preparing to unpack .../38-python3-jaraco.context_6.0.0-1_all.deb ... Unpacking python3-jaraco.context (6.0.0-1) ... Selecting previously unselected package python3-jaraco.functools. Preparing to unpack .../39-python3-jaraco.functools_4.1.0-1_all.deb ... Unpacking python3-jaraco.functools (4.1.0-1) ... Selecting previously unselected package python3-pkg-resources. Preparing to unpack .../40-python3-pkg-resources_75.8.0-1_all.deb ... Unpacking python3-pkg-resources (75.8.0-1) ... Selecting previously unselected package python3-jaraco.text. Preparing to unpack .../41-python3-jaraco.text_4.0.0-1_all.deb ... Unpacking python3-jaraco.text (4.0.0-1) ... Selecting previously unselected package python3-zipp. Preparing to unpack .../42-python3-zipp_3.21.0-1_all.deb ... Unpacking python3-zipp (3.21.0-1) ... Selecting previously unselected package python3-setuptools. Preparing to unpack .../43-python3-setuptools_75.8.0-1_all.deb ... Unpacking python3-setuptools (75.8.0-1) ... Selecting previously unselected package dh-python. Preparing to unpack .../44-dh-python_6.20250108_all.deb ... Unpacking dh-python (6.20250108) ... Selecting previously unselected package libcares2:arm64. Preparing to unpack .../45-libcares2_1.34.4-2.1_arm64.deb ... Unpacking libcares2:arm64 (1.34.4-2.1) ... Selecting previously unselected package libcom-err2:arm64. Preparing to unpack .../46-libcom-err2_1.47.2-1_arm64.deb ... Unpacking libcom-err2:arm64 (1.47.2-1) ... Selecting previously unselected package libkrb5support0:arm64. Preparing to unpack .../47-libkrb5support0_1.21.3-4_arm64.deb ... Unpacking libkrb5support0:arm64 (1.21.3-4) ... Selecting previously unselected package libk5crypto3:arm64. Preparing to unpack .../48-libk5crypto3_1.21.3-4_arm64.deb ... Unpacking libk5crypto3:arm64 (1.21.3-4) ... Selecting previously unselected package libkeyutils1:arm64. Preparing to unpack .../49-libkeyutils1_1.6.3-4_arm64.deb ... Unpacking libkeyutils1:arm64 (1.6.3-4) ... Selecting previously unselected package libkrb5-3:arm64. Preparing to unpack .../50-libkrb5-3_1.21.3-4_arm64.deb ... Unpacking libkrb5-3:arm64 (1.21.3-4) ... Selecting previously unselected package libgssapi-krb5-2:arm64. Preparing to unpack .../51-libgssapi-krb5-2_1.21.3-4_arm64.deb ... Unpacking libgssapi-krb5-2:arm64 (1.21.3-4) ... Selecting previously unselected package libjs-jquery. Preparing to unpack .../52-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 libjs-jquery-hotkeys. Preparing to unpack .../53-libjs-jquery-hotkeys_0.2.0-1_all.deb ... Unpacking libjs-jquery-hotkeys (0.2.0-1) ... Selecting previously unselected package libjs-jquery-isonscreen. Preparing to unpack .../54-libjs-jquery-isonscreen_1.2.0-1.1_all.deb ... Unpacking libjs-jquery-isonscreen (1.2.0-1.1) ... Selecting previously unselected package libjs-jquery-metadata. Preparing to unpack .../55-libjs-jquery-metadata_12-4_all.deb ... Unpacking libjs-jquery-metadata (12-4) ... Selecting previously unselected package libjs-jquery-tablesorter. Preparing to unpack .../56-libjs-jquery-tablesorter_1%3a2.31.3+dfsg1-4_all.deb ... Unpacking libjs-jquery-tablesorter (1:2.31.3+dfsg1-4) ... Selecting previously unselected package libjs-jquery-throttle-debounce. Preparing to unpack .../57-libjs-jquery-throttle-debounce_1.1+dfsg.1-2_all.deb ... Unpacking libjs-jquery-throttle-debounce (1.1+dfsg.1-2) ... Selecting previously unselected package libtirpc-common. Preparing to unpack .../58-libtirpc-common_1.3.4+ds-1.3_all.deb ... Unpacking libtirpc-common (1.3.4+ds-1.3) ... Selecting previously unselected package libtirpc3t64:arm64. Preparing to unpack .../59-libtirpc3t64_1.3.4+ds-1.3+b1_arm64.deb ... Adding 'diversion of /lib/aarch64-linux-gnu/libtirpc.so.3 to /lib/aarch64-linux-gnu/libtirpc.so.3.usr-is-merged by libtirpc3t64' Adding 'diversion of /lib/aarch64-linux-gnu/libtirpc.so.3.0.0 to /lib/aarch64-linux-gnu/libtirpc.so.3.0.0.usr-is-merged by libtirpc3t64' Unpacking libtirpc3t64:arm64 (1.3.4+ds-1.3+b1) ... Selecting previously unselected package libnsl2:arm64. Preparing to unpack .../60-libnsl2_1.3.0-3+b3_arm64.deb ... Unpacking libnsl2:arm64 (1.3.0-3+b3) ... Selecting previously unselected package libpython3.12-stdlib:arm64. Preparing to unpack .../61-libpython3.12-stdlib_3.12.9-1_arm64.deb ... Unpacking libpython3.12-stdlib:arm64 (3.12.9-1) ... Selecting previously unselected package python3-packaging. Preparing to unpack .../62-python3-packaging_24.2-1_all.deb ... Unpacking python3-packaging (24.2-1) ... Selecting previously unselected package python3-pyproject-hooks. Preparing to unpack .../63-python3-pyproject-hooks_1.2.0-1_all.deb ... Unpacking python3-pyproject-hooks (1.2.0-1) ... Selecting previously unselected package python3-toml. Preparing to unpack .../64-python3-toml_0.10.2-1_all.deb ... Unpacking python3-toml (0.10.2-1) ... Selecting previously unselected package python3-wheel. Preparing to unpack .../65-python3-wheel_0.45.1-1_all.deb ... Unpacking python3-wheel (0.45.1-1) ... Selecting previously unselected package python3-build. Preparing to unpack .../66-python3-build_1.2.2-1_all.deb ... Unpacking python3-build (1.2.2-1) ... Selecting previously unselected package python3-installer. Preparing to unpack .../67-python3-installer_0.7.0+dfsg1-3_all.deb ... Unpacking python3-installer (0.7.0+dfsg1-3) ... Selecting previously unselected package pybuild-plugin-pyproject. Preparing to unpack .../68-pybuild-plugin-pyproject_6.20250108_all.deb ... Unpacking pybuild-plugin-pyproject (6.20250108) ... Selecting previously unselected package python3-idna. Preparing to unpack .../69-python3-idna_3.10-1_all.deb ... Unpacking python3-idna (3.10-1) ... Selecting previously unselected package python3-cffi-backend:arm64. Preparing to unpack .../70-python3-cffi-backend_1.17.1-2+b1_arm64.deb ... Unpacking python3-cffi-backend:arm64 (1.17.1-2+b1) ... Selecting previously unselected package python3-pycares. Preparing to unpack .../71-python3-pycares_4.4.0-2+b3_arm64.deb ... Unpacking python3-pycares (4.4.0-2+b3) ... Selecting previously unselected package python3-aiodns. Preparing to unpack .../72-python3-aiodns_3.2.0-2_all.deb ... Unpacking python3-aiodns (3.2.0-2) ... Selecting previously unselected package python3-aiohappyeyeballs. Preparing to unpack .../73-python3-aiohappyeyeballs_2.4.6-1_all.deb ... Unpacking python3-aiohappyeyeballs (2.4.6-1) ... Selecting previously unselected package python3-multidict. Preparing to unpack .../74-python3-multidict_6.1.0-1+b1_arm64.deb ... Unpacking python3-multidict (6.1.0-1+b1) ... Selecting previously unselected package python3-yarl. Preparing to unpack .../75-python3-yarl_1.13.1-1+b1_arm64.deb ... Unpacking python3-yarl (1.13.1-1+b1) ... Selecting previously unselected package python3-async-timeout. Preparing to unpack .../76-python3-async-timeout_5.0.1-1_all.deb ... Unpacking python3-async-timeout (5.0.1-1) ... Selecting previously unselected package python3-frozenlist. Preparing to unpack .../77-python3-frozenlist_1.5.0-1+b2_arm64.deb ... Unpacking python3-frozenlist (1.5.0-1+b2) ... Selecting previously unselected package python3-aiosignal. Preparing to unpack .../78-python3-aiosignal_1.3.2-1_all.deb ... Unpacking python3-aiosignal (1.3.2-1) ... Selecting previously unselected package python3-attr. Preparing to unpack .../79-python3-attr_25.1.0-1_all.deb ... Unpacking python3-attr (25.1.0-1) ... Selecting previously unselected package python3-aiohttp. Preparing to unpack .../80-python3-aiohttp_3.10.11-1_arm64.deb ... Unpacking python3-aiohttp (3.10.11-1) ... Selecting previously unselected package python3.12. Preparing to unpack .../81-python3.12_3.12.9-1_arm64.deb ... Unpacking python3.12 (3.12.9-1) ... Selecting previously unselected package python3-all. Preparing to unpack .../82-python3-all_3.13.1-2_arm64.deb ... Unpacking python3-all (3.13.1-2) ... Selecting previously unselected package python3-async-generator. Preparing to unpack .../83-python3-async-generator_1.10-4_all.deb ... Unpacking python3-async-generator (1.10-4) ... Selecting previously unselected package python3-iniconfig. Preparing to unpack .../84-python3-iniconfig_1.1.1-2_all.deb ... Unpacking python3-iniconfig (1.1.1-2) ... Selecting previously unselected package python3-pluggy. Preparing to unpack .../85-python3-pluggy_1.5.0-1_all.deb ... Unpacking python3-pluggy (1.5.0-1) ... Selecting previously unselected package python3-pytest. Preparing to unpack .../86-python3-pytest_8.3.4-1_all.deb ... Unpacking python3-pytest (8.3.4-1) ... Selecting previously unselected package python3-pytest-asyncio. Preparing to unpack .../87-python3-pytest-asyncio_0.25.1-1_all.deb ... Unpacking python3-pytest-asyncio (0.25.1-1) ... Selecting previously unselected package python3-aresponses. Preparing to unpack .../88-python3-aresponses_3.0.0-2_all.deb ... Unpacking python3-aresponses (3.0.0-2) ... Selecting previously unselected package python3-coverage. Preparing to unpack .../89-python3-coverage_7.6.0+dfsg1-2+b1_arm64.deb ... Unpacking python3-coverage (7.6.0+dfsg1-2+b1) ... Selecting previously unselected package python3-dateutil. Preparing to unpack .../90-python3-dateutil_2.9.0-3_all.deb ... Unpacking python3-dateutil (2.9.0-3) ... Selecting previously unselected package python3-freezegun. Preparing to unpack .../91-python3-freezegun_1.5.1-1.2_all.deb ... Unpacking python3-freezegun (1.5.1-1.2) ... Selecting previously unselected package python3-poetry-core. Preparing to unpack .../92-python3-poetry-core_2.0.1-2_all.deb ... Unpacking python3-poetry-core (2.0.1-2) ... Selecting previously unselected package python3-pytest-cov. Preparing to unpack .../93-python3-pytest-cov_5.0.0-1_all.deb ... Unpacking python3-pytest-cov (5.0.0-1) ... Selecting previously unselected package python3-pytest-freezer. Preparing to unpack .../94-python3-pytest-freezer_0.4.9-1_all.deb ... Unpacking python3-pytest-freezer (0.4.9-1) ... Selecting previously unselected package python3-syrupy. Preparing to unpack .../95-python3-syrupy_4.8.1-1_all.deb ... Unpacking python3-syrupy (4.8.1-1) ... Setting up media-types (11.0.0) ... Setting up libpipeline1:arm64 (1.5.8-1) ... Setting up libkeyutils1:arm64 (1.6.3-4) ... Setting up libicu72:arm64 (72.1-6) ... Setting up bsdextrautils (2.40.4-4) ... Setting up libmagic-mgc (1:5.45-3+b1) ... Setting up libarchive-zip-perl (1.68-1) ... Setting up libtirpc-common (1.3.4+ds-1.3) ... Setting up libdebhelper-perl (13.24.1) ... Setting up libmagic1t64:arm64 (1:5.45-3+b1) ... Setting up libpython3.12-minimal:arm64 (3.12.9-1) ... Setting up gettext-base (0.23.1-1) ... Setting up m4 (1.4.19-5) ... Setting up libcom-err2:arm64 (1.47.2-1) ... Setting up file (1:5.45-3+b1) ... Setting up libjs-jquery-throttle-debounce (1.1+dfsg.1-2) ... Setting up libelf1t64:arm64 (0.192-4) ... Setting up libkrb5support0:arm64 (1.21.3-4) ... Setting up tzdata (2025a-2) ... Current default time zone: 'Etc/UTC' Local time is now: Fri Mar 27 05:28:48 UTC 2026. Universal Time is now: Fri Mar 27 05:28:48 UTC 2026. Run 'dpkg-reconfigure tzdata' if you wish to change it. Setting up autotools-dev (20220109.1) ... Setting up libunistring5:arm64 (1.3-1) ... Setting up libcares2:arm64 (1.34.4-2.1) ... Setting up autopoint (0.23.1-1) ... Setting up libk5crypto3:arm64 (1.21.3-4) ... Setting up autoconf (2.72-3) ... Setting up libffi8:arm64 (3.4.7-1) ... Setting up dwz (0.15-1+b1) ... Setting up sensible-utils (0.0.24) ... Setting up libuchardet0:arm64 (0.0.8-1+b2) ... Setting up netbase (6.4) ... Setting up libkrb5-3:arm64 (1.21.3-4) ... Setting up libjs-jquery (3.6.1+dfsg+~3.5.14-1) ... Setting up libjs-jquery-hotkeys (0.2.0-1) ... Setting up readline-common (8.2-6) ... Setting up libxml2:arm64 (2.12.7+dfsg+really2.9.14-0.2+b1) ... Setting up automake (1:1.17-3) ... update-alternatives: using /usr/bin/automake-1.17 to provide /usr/bin/automake (automake) in auto mode Setting up libfile-stripnondeterminism-perl (1.14.1-2) ... Setting up python3.12-minimal (3.12.9-1) ... Setting up gettext (0.23.1-1) ... Setting up libtool (2.5.4-3) ... Setting up intltool-debian (0.35.0+20060710.6) ... Setting up dh-autoreconf (20) ... Setting up libjs-jquery-metadata (12-4) ... Setting up libjs-jquery-isonscreen (1.2.0-1.1) ... Setting up libgssapi-krb5-2:arm64 (1.21.3-4) ... Setting up libreadline8t64:arm64 (8.2-6) ... Setting up dh-strip-nondeterminism (1.14.1-2) ... Setting up libjs-jquery-tablesorter (1:2.31.3+dfsg1-4) ... Setting up groff-base (1.23.0-7) ... Setting up libpython3.13-stdlib:arm64 (3.13.2-1) ... Setting up libpython3-stdlib:arm64 (3.13.1-2) ... Setting up libtirpc3t64:arm64 (1.3.4+ds-1.3+b1) ... Setting up python3.13 (3.13.2-1) ... Setting up po-debconf (1.0.21+nmu1) ... Setting up python3 (3.13.1-2) ... Setting up python3-zipp (3.21.0-1) ... Setting up python3-autocommand (2.2.2-3) ... Setting up man-db (2.13.0-1) ... Not building database; man-db/auto-update is not 'true'. Setting up python3-wheel (0.45.1-1) ... Setting up python3-multidict (6.1.0-1+b1) ... Setting up python3-frozenlist (1.5.0-1+b2) ... Setting up python3-aiosignal (1.3.2-1) ... Setting up python3-async-timeout (5.0.1-1) ... Setting up python3-packaging (24.2-1) ... Setting up python3-pyproject-hooks (1.2.0-1) ... Setting up python3-poetry-core (2.0.1-2) ... Setting up python3-idna (3.10-1) ... Setting up python3-typing-extensions (4.12.2-2) ... Setting up python3-aiohappyeyeballs (2.4.6-1) ... Setting up python3-toml (0.10.2-1) ... Setting up python3-installer (0.7.0+dfsg1-3) ... Setting up python3-pluggy (1.5.0-1) ... Setting up libnsl2:arm64 (1.3.0-3+b3) ... Setting up python3-dateutil (2.9.0-3) ... Setting up python3-build (1.2.2-1) ... Setting up python3-yarl (1.13.1-1+b1) ... Setting up python3-freezegun (1.5.1-1.2) ... Setting up python3-cffi-backend:arm64 (1.17.1-2+b1) ... Setting up python3-more-itertools (10.6.0-1) ... Setting up python3-iniconfig (1.1.1-2) ... Setting up python3-attr (25.1.0-1) ... Setting up python3-jaraco.functools (4.1.0-1) ... Setting up libpython3.12-stdlib:arm64 (3.12.9-1) ... Setting up python3-async-generator (1.10-4) ... Setting up python3-jaraco.context (6.0.0-1) ... Setting up python3-pytest (8.3.4-1) ... Setting up python3-syrupy (4.8.1-1) ... Setting up python3.12 (3.12.9-1) ... Setting up python3-typeguard (4.4.1-1) ... Setting up python3-aiohttp (3.10.11-1) ... Setting up python3-all (3.13.1-2) ... Setting up python3-coverage (7.6.0+dfsg1-2+b1) ... Setting up debhelper (13.24.1) ... Setting up python3-pytest-cov (5.0.0-1) ... Setting up python3-inflect (7.3.1-2) ... Setting up python3-jaraco.text (4.0.0-1) ... Setting up python3-pytest-freezer (0.4.9-1) ... Setting up python3-pytest-asyncio (0.25.1-1) ... Setting up python3-pycares (4.4.0-2+b3) ... Setting up python3-pkg-resources (75.8.0-1) ... Setting up python3-setuptools (75.8.0-1) ... Setting up python3-aresponses (3.0.0-2) ... Setting up python3-aiodns (3.2.0-2) ... Setting up dh-python (6.20250108) ... Setting up pybuild-plugin-pyproject (6.20250108) ... Processing triggers for libc-bin (2.40-7) ... 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/forecast-solar-4.0.0/ && 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 > ../forecast-solar_4.0.0-1_source.changes dpkg-buildpackage: info: source package forecast-solar dpkg-buildpackage: info: source version 4.0.0-1 dpkg-buildpackage: info: source distribution unstable dpkg-buildpackage: info: source changed by Edward Betts dpkg-source --before-build . dpkg-buildpackage: info: host architecture arm64 debian/rules clean dh clean --buildsystem=pybuild dh_auto_clean -O--buildsystem=pybuild dh_autoreconf_clean -O--buildsystem=pybuild dh_clean -O--buildsystem=pybuild 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 dh_auto_build -O--buildsystem=pybuild I: pybuild plugin_pyproject:129: Building wheel for python3.12 with "build" module I: pybuild base:311: python3.12 -m build --skip-dependency-check --no-isolation --wheel --outdir /build/reproducible-path/forecast-solar-4.0.0/.pybuild/cpython3_3.12 * Building wheel... Successfully built forecast_solar-0.0.0-py3-none-any.whl I: pybuild plugin_pyproject:144: Unpacking wheel built for python3.12 with "installer" module I: pybuild plugin_pyproject:129: Building wheel for python3.13 with "build" module I: pybuild base:311: python3.13 -m build --skip-dependency-check --no-isolation --wheel --outdir /build/reproducible-path/forecast-solar-4.0.0/.pybuild/cpython3_3.13 * Building wheel... Successfully built forecast_solar-0.0.0-py3-none-any.whl I: pybuild plugin_pyproject:144: Unpacking wheel built for python3.13 with "installer" module dh_auto_test -O--buildsystem=pybuild I: pybuild base:311: cd /build/reproducible-path/forecast-solar-4.0.0/.pybuild/cpython3_3.12/build; python3.12 -m pytest --no-cov /usr/lib/python3/dist-packages/pytest_asyncio/plugin.py:207: PytestDeprecationWarning: The configuration option "asyncio_default_fixture_loop_scope" is unset. The event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: "function", "class", "module", "package", "session" warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET)) ============================= test session starts ============================== platform linux -- Python 3.12.9, pytest-8.3.4, pluggy-1.5.0 rootdir: /build/reproducible-path/forecast-solar-4.0.0/.pybuild/cpython3_3.12/build configfile: pyproject.toml plugins: syrupy-4.8.1, pytest_freezer-0.4.9, cov-5.0.0, aresponses-3.0.0, asyncio-0.25.1, typeguard-4.4.1 asyncio: mode=Mode.AUTO, asyncio_default_fixture_loop_scope=None collected 15 items tests/test_exceptions.py FFFF. [ 33%] tests/test_forecast.py FF... [ 66%] tests/test_models.py FFFFF [100%] =================================== FAILURES =================================== _______________________________ test_status_400 ________________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_status_400( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test response status 400.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=400, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) with pytest.raises(ForecastSolarRequestError): > assert await forecast_client._request("test") tests/test_exceptions.py:38: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError _______________________________ test_status_401 ________________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_status_401( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test response status 401 or 403.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=401, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) with pytest.raises(ForecastSolarAuthenticationError): > assert await forecast_client._request("test") tests/test_exceptions.py:61: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError _______________________________ test_status_422 ________________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_status_422( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test response status 422.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=422, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) with pytest.raises(ForecastSolarConfigError): > assert await forecast_client._request("test") tests/test_exceptions.py:84: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError _______________________________ test_status_429 ________________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_status_429( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test response status 429.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=429, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("ratelimit.json"), ), ) with pytest.raises(ForecastSolarRatelimitError): > assert await forecast_client._request("test") tests/test_exceptions.py:107: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ______________________________ test_json_request _______________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_json_request( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test JSON response is handled correctly.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) > response = await forecast_client._request("test") tests/test_forecast.py:42: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ____________________________ test_internal_session _____________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m...ing_evening=None, horizon='0,0,0,10,10,20,20,30,30', session=None, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = async def test_internal_session(aresponses: ResponsesMockServer) -> None: """Test internal session is handled correctly.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) async with ForecastSolar( latitude=52.16, longitude=4.47, declination=20, azimuth=10, kwp=2.160, damping=0, horizon="0,0,0,10,10,20,20,30,30", ) as client: > await client._request("test") tests/test_forecast.py:72: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m...ing_evening=None, horizon='0,0,0,10,10,20,20,30,30', session=None, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ___________________________ test_estimated_forecast ____________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = snapshot = SnapshotAssertion(name='snapshot', num_executions=0) forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) @pytest.mark.freeze_time("2024-04-26T12:00:00+02:00") async def test_estimated_forecast( aresponses: ResponsesMockServer, snapshot: SnapshotAssertion, forecast_client: ForecastSolar, ) -> None: """Test estimated forecast.""" aresponses.add( "api.forecast.solar", "/estimate/52.16/4.47/20/10/2.16", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) > forecast: Estimate = await forecast_client.estimate() tests/test_models.py:35: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:208: in estimate data = await self._request( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError __________________ test_estimated_forecast_with_subscription ___________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = snapshot = SnapshotAssertion(name='snapshot', num_executions=0) forecast_key_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) @pytest.mark.freeze_time("2024-04-27T07:00:00+02:00") async def test_estimated_forecast_with_subscription( aresponses: ResponsesMockServer, snapshot: SnapshotAssertion, forecast_key_client: ForecastSolar, ) -> None: """Test estimated forecast.""" aresponses.add( "api.forecast.solar", "/myapikey/estimate/52.16/4.47/20/10/2.16", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "60", "X-Ratelimit-Period": "3600", }, text=load_fixtures("forecast_personal.json"), ), ) > forecast: Estimate = await forecast_key_client.estimate() tests/test_models.py:81: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:208: in estimate data = await self._request( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError __________ test_estimated_forecast_with_subscription_and_actual_value __________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = snapshot = SnapshotAssertion(name='snapshot', num_executions=0) forecast_key_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) @pytest.mark.freeze_time("2024-04-27T07:00:00+02:00") async def test_estimated_forecast_with_subscription_and_actual_value( aresponses: ResponsesMockServer, snapshot: SnapshotAssertion, forecast_key_client: ForecastSolar, ) -> None: """Test estimated forecast.""" aresponses.add( "api.forecast.solar", "/myapikey/estimate/52.16/4.47/20/10/2.16", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "60", "X-Ratelimit-Period": "3600", }, text=load_fixtures("forecast_personal.json"), ), ) > forecast: Estimate = await forecast_key_client.estimate(actual=2.300) tests/test_models.py:127: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:208: in estimate data = await self._request( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ___________________________ test_api_key_validation ____________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'info' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_key_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) async def test_api_key_validation( aresponses: ResponsesMockServer, forecast_key_client: ForecastSolar, ) -> None: """Test API key validation.""" aresponses.add( "api.forecast.solar", "/myapikey/info", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("validate_key.json"), ), ) > assert await forecast_key_client.validate_api_key() is True tests/test_models.py:160: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:180: in validate_api_key await self._request("info", rate_limit=False) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'info' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ____________________________ test_plane_validation _____________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'check/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_plane_validation( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test plane validation.""" aresponses.add( "api.forecast.solar", "/check/52.16/4.47/20/10/2.16", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("validate_plane.json"), ), ) > assert await forecast_client.validate_plane() is True tests/test_models.py:182: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:163: in validate_plane await self._request( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'check/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError --------------------------- snapshot report summary ---------------------------- 3 snapshots unused. Re-run pytest with --snapshot-update to delete unused snapshots. =========================== short test summary info ============================ FAILED tests/test_exceptions.py::test_status_400 - forecast_solar.exceptions.... FAILED tests/test_exceptions.py::test_status_401 - forecast_solar.exceptions.... FAILED tests/test_exceptions.py::test_status_422 - forecast_solar.exceptions.... FAILED tests/test_exceptions.py::test_status_429 - forecast_solar.exceptions.... FAILED tests/test_forecast.py::test_json_request - forecast_solar.exceptions.... FAILED tests/test_forecast.py::test_internal_session - forecast_solar.excepti... FAILED tests/test_models.py::test_estimated_forecast - forecast_solar.excepti... FAILED tests/test_models.py::test_estimated_forecast_with_subscription - fore... FAILED tests/test_models.py::test_estimated_forecast_with_subscription_and_actual_value FAILED tests/test_models.py::test_api_key_validation - forecast_solar.excepti... FAILED tests/test_models.py::test_plane_validation - forecast_solar.exception... ========================= 11 failed, 4 passed in 0.87s ========================= E: pybuild pybuild:389: test: plugin pyproject failed with: exit code=1: cd /build/reproducible-path/forecast-solar-4.0.0/.pybuild/cpython3_3.12/build; python3.12 -m pytest --no-cov I: pybuild base:311: cd /build/reproducible-path/forecast-solar-4.0.0/.pybuild/cpython3_3.13/build; python3.13 -m pytest --no-cov /usr/lib/python3/dist-packages/pytest_asyncio/plugin.py:207: PytestDeprecationWarning: The configuration option "asyncio_default_fixture_loop_scope" is unset. The event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: "function", "class", "module", "package", "session" warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET)) ============================= test session starts ============================== platform linux -- Python 3.13.2, pytest-8.3.4, pluggy-1.5.0 rootdir: /build/reproducible-path/forecast-solar-4.0.0/.pybuild/cpython3_3.13/build configfile: pyproject.toml plugins: syrupy-4.8.1, pytest_freezer-0.4.9, cov-5.0.0, aresponses-3.0.0, asyncio-0.25.1, typeguard-4.4.1 asyncio: mode=Mode.AUTO, asyncio_default_fixture_loop_scope=None collected 15 items tests/test_exceptions.py FFFF. [ 33%] tests/test_forecast.py FF... [ 66%] tests/test_models.py FFFFF [100%] =================================== FAILURES =================================== _______________________________ test_status_400 ________________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_status_400( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test response status 400.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=400, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) with pytest.raises(ForecastSolarRequestError): > assert await forecast_client._request("test") tests/test_exceptions.py:38: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError _______________________________ test_status_401 ________________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_status_401( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test response status 401 or 403.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=401, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) with pytest.raises(ForecastSolarAuthenticationError): > assert await forecast_client._request("test") tests/test_exceptions.py:61: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError _______________________________ test_status_422 ________________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_status_422( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test response status 422.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=422, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) with pytest.raises(ForecastSolarConfigError): > assert await forecast_client._request("test") tests/test_exceptions.py:84: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError _______________________________ test_status_429 ________________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_status_429( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test response status 429.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=429, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("ratelimit.json"), ), ) with pytest.raises(ForecastSolarRatelimitError): > assert await forecast_client._request("test") tests/test_exceptions.py:107: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ______________________________ test_json_request _______________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_json_request( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test JSON response is handled correctly.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) > response = await forecast_client._request("test") tests/test_forecast.py:42: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ____________________________ test_internal_session _____________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m...ing_evening=None, horizon='0,0,0,10,10,20,20,30,30', session=None, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = async def test_internal_session(aresponses: ResponsesMockServer) -> None: """Test internal session is handled correctly.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) async with ForecastSolar( latitude=52.16, longitude=4.47, declination=20, azimuth=10, kwp=2.160, damping=0, horizon="0,0,0,10,10,20,20,30,30", ) as client: > await client._request("test") tests/test_forecast.py:72: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m...ing_evening=None, horizon='0,0,0,10,10,20,20,30,30', session=None, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ___________________________ test_estimated_forecast ____________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = snapshot = SnapshotAssertion(name='snapshot', num_executions=0) forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) @pytest.mark.freeze_time("2024-04-26T12:00:00+02:00") async def test_estimated_forecast( aresponses: ResponsesMockServer, snapshot: SnapshotAssertion, forecast_client: ForecastSolar, ) -> None: """Test estimated forecast.""" aresponses.add( "api.forecast.solar", "/estimate/52.16/4.47/20/10/2.16", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) > forecast: Estimate = await forecast_client.estimate() tests/test_models.py:35: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:208: in estimate data = await self._request( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError __________________ test_estimated_forecast_with_subscription ___________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = snapshot = SnapshotAssertion(name='snapshot', num_executions=0) forecast_key_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) @pytest.mark.freeze_time("2024-04-27T07:00:00+02:00") async def test_estimated_forecast_with_subscription( aresponses: ResponsesMockServer, snapshot: SnapshotAssertion, forecast_key_client: ForecastSolar, ) -> None: """Test estimated forecast.""" aresponses.add( "api.forecast.solar", "/myapikey/estimate/52.16/4.47/20/10/2.16", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "60", "X-Ratelimit-Period": "3600", }, text=load_fixtures("forecast_personal.json"), ), ) > forecast: Estimate = await forecast_key_client.estimate() tests/test_models.py:81: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:208: in estimate data = await self._request( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError __________ test_estimated_forecast_with_subscription_and_actual_value __________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = snapshot = SnapshotAssertion(name='snapshot', num_executions=0) forecast_key_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) @pytest.mark.freeze_time("2024-04-27T07:00:00+02:00") async def test_estimated_forecast_with_subscription_and_actual_value( aresponses: ResponsesMockServer, snapshot: SnapshotAssertion, forecast_key_client: ForecastSolar, ) -> None: """Test estimated forecast.""" aresponses.add( "api.forecast.solar", "/myapikey/estimate/52.16/4.47/20/10/2.16", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "60", "X-Ratelimit-Period": "3600", }, text=load_fixtures("forecast_personal.json"), ), ) > forecast: Estimate = await forecast_key_client.estimate(actual=2.300) tests/test_models.py:127: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:208: in estimate data = await self._request( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ___________________________ test_api_key_validation ____________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'info' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_key_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) async def test_api_key_validation( aresponses: ResponsesMockServer, forecast_key_client: ForecastSolar, ) -> None: """Test API key validation.""" aresponses.add( "api.forecast.solar", "/myapikey/info", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("validate_key.json"), ), ) > assert await forecast_key_client.validate_api_key() is True tests/test_models.py:160: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:180: in validate_api_key await self._request("info", rate_limit=False) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'info' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ____________________________ test_plane_validation _____________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'check/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_plane_validation( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test plane validation.""" aresponses.add( "api.forecast.solar", "/check/52.16/4.47/20/10/2.16", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("validate_plane.json"), ), ) > assert await forecast_client.validate_plane() is True tests/test_models.py:182: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:163: in validate_plane await self._request( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'check/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError --------------------------- snapshot report summary ---------------------------- 3 snapshots unused. Re-run pytest with --snapshot-update to delete unused snapshots. =========================== short test summary info ============================ FAILED tests/test_exceptions.py::test_status_400 - forecast_solar.exceptions.... FAILED tests/test_exceptions.py::test_status_401 - forecast_solar.exceptions.... FAILED tests/test_exceptions.py::test_status_422 - forecast_solar.exceptions.... FAILED tests/test_exceptions.py::test_status_429 - forecast_solar.exceptions.... FAILED tests/test_forecast.py::test_json_request - forecast_solar.exceptions.... FAILED tests/test_forecast.py::test_internal_session - forecast_solar.excepti... FAILED tests/test_models.py::test_estimated_forecast - forecast_solar.excepti... FAILED tests/test_models.py::test_estimated_forecast_with_subscription - fore... FAILED tests/test_models.py::test_estimated_forecast_with_subscription_and_actual_value FAILED tests/test_models.py::test_api_key_validation - forecast_solar.excepti... FAILED tests/test_models.py::test_plane_validation - forecast_solar.exception... ========================= 11 failed, 4 passed in 0.34s ========================= E: pybuild pybuild:389: test: plugin pyproject failed with: exit code=1: cd /build/reproducible-path/forecast-solar-4.0.0/.pybuild/cpython3_3.13/build; python3.13 -m pytest --no-cov dh_auto_test: error: pybuild --test --test-pytest -i python{version} -p "3.12 3.13" returned exit code 13 make: *** [debian/rules:5: binary] Error 25 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/395853 and its subdirectories Starting cleanup. All cleanup done. Fri Mar 27 05:29:46 UTC 2026 - reproducible_build.sh stopped running as /tmp/jenkins-script-RlLliAYv, removing. /srv/reproducible-results/rbuild-debian/r-b-build.65nWBFE2: total 16 drwxr-xr-x 2 jenkins jenkins 4096 Feb 21 23:06 b1 drwxr-xr-x 2 jenkins jenkins 4096 Feb 21 23:05 b2 -rw-r--r-- 1 jenkins jenkins 2518 Feb 21 12:48 forecast-solar_4.0.0-1.dsc -rw------- 1 jenkins jenkins 3676 Feb 21 23:05 rbuildlog.dWaKVOu /srv/reproducible-results/rbuild-debian/r-b-build.65nWBFE2/b1: total 184 -rw-r--r-- 1 jenkins jenkins 186048 Feb 21 23:06 build.log /srv/reproducible-results/rbuild-debian/r-b-build.65nWBFE2/b2: total 0 Fri Feb 21 23:06:48 UTC 2025 I: Deleting $TMPDIR on codethink03-arm64.debian.net. I: pbuilder: network access will be disabled during build I: Current time: Thu Mar 26 17:28:03 -12 2026 I: pbuilder-time-stamp: 1774589283 I: Building the build Environment I: extracting base tarball [/var/cache/pbuilder/unstable-reproducible-base.tgz] I: copying local configuration W: --override-config is not set; not updating apt.conf Read the manpage for details. I: mounting /proc filesystem I: mounting /sys filesystem I: creating /{dev,run}/shm I: mounting /dev/pts filesystem I: redirecting /dev/ptmx to /dev/pts/ptmx I: policy-rc.d already exists I: Copying source file I: copying [forecast-solar_4.0.0-1.dsc] I: copying [./forecast-solar_4.0.0.orig.tar.gz] I: copying [./forecast-solar_4.0.0-1.debian.tar.xz] I: Extracting source dpkg-source: warning: cannot verify inline signature for ./forecast-solar_4.0.0-1.dsc: unsupported subcommand dpkg-source: info: extracting forecast-solar in forecast-solar-4.0.0 dpkg-source: info: unpacking forecast-solar_4.0.0.orig.tar.gz dpkg-source: info: unpacking forecast-solar_4.0.0-1.debian.tar.xz I: Not using root during the build. I: Installing the build-deps I: user script /srv/workspace/pbuilder/395853/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='arm64' DEBIAN_FRONTEND='noninteractive' DEB_BUILD_OPTIONS='buildinfo=+all reproducible=+all parallel=12 ' DISTRIBUTION='unstable' HOME='/root' HOST_ARCH='arm64' IFS=' ' INVOCATION_ID='e5b845bcbfa04ba5a31c60de24f672d0' 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='395853' 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.65nWBFE2/pbuilderrc_oT0O --distribution unstable --hookdir /etc/pbuilder/first-build-hooks --debbuildopts -b --basetgz /var/cache/pbuilder/unstable-reproducible-base.tgz --buildresult /srv/reproducible-results/rbuild-debian/r-b-build.65nWBFE2/b1 --logfile b1/build.log forecast-solar_4.0.0-1.dsc' SUDO_GID='109' SUDO_UID='104' SUDO_USER='jenkins' TERM='unknown' TZ='/usr/share/zoneinfo/Etc/GMT+12' USER='root' _='/usr/bin/systemd-run' http_proxy='http://192.168.101.4:3128' I: uname -a Linux codethink03-arm64 6.1.0-31-cloud-arm64 #1 SMP Debian 6.1.128-1 (2025-02-07) aarch64 GNU/Linux I: ls -l /bin lrwxrwxrwx 1 root root 7 Nov 22 2024 /bin -> usr/bin I: user script /srv/workspace/pbuilder/395853/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: arm64 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, pybuild-plugin-pyproject, python3-all, python3-poetry-core, python3-aiodns, python3-aiohttp, python3-aresponses, python3-pytest, python3-pytest-asyncio, python3-pytest-cov, python3-pytest-freezer, python3-syrupy dpkg-deb: building package 'pbuilder-satisfydepends-dummy' in '/tmp/satisfydepends-aptitude/pbuilder-satisfydepends-dummy.deb'. Selecting previously unselected package pbuilder-satisfydepends-dummy. (Reading database ... 19889 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 pybuild-plugin-pyproject; however: Package pybuild-plugin-pyproject is not installed. pbuilder-satisfydepends-dummy depends on python3-all; however: Package python3-all is not installed. pbuilder-satisfydepends-dummy depends on python3-poetry-core; however: Package python3-poetry-core is not installed. pbuilder-satisfydepends-dummy depends on python3-aiodns; however: Package python3-aiodns is not installed. pbuilder-satisfydepends-dummy depends on python3-aiohttp; however: Package python3-aiohttp is not installed. pbuilder-satisfydepends-dummy depends on python3-aresponses; however: Package python3-aresponses is not installed. pbuilder-satisfydepends-dummy depends on python3-pytest; however: Package python3-pytest is not installed. pbuilder-satisfydepends-dummy depends on python3-pytest-asyncio; however: Package python3-pytest-asyncio is not installed. pbuilder-satisfydepends-dummy depends on python3-pytest-cov; however: Package python3-pytest-cov is not installed. pbuilder-satisfydepends-dummy depends on python3-pytest-freezer; however: Package python3-pytest-freezer is not installed. pbuilder-satisfydepends-dummy depends on python3-syrupy; however: Package python3-syrupy 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} debhelper{a} dh-autoreconf{a} dh-python{a} dh-strip-nondeterminism{a} dwz{a} file{a} gettext{a} gettext-base{a} groff-base{a} intltool-debian{a} libarchive-zip-perl{a} libcares2{a} libcom-err2{a} libdebhelper-perl{a} libelf1t64{a} libexpat1{a} libffi8{a} libfile-stripnondeterminism-perl{a} libgssapi-krb5-2{a} libicu72{a} libjs-jquery{a} libjs-jquery-hotkeys{a} libjs-jquery-isonscreen{a} libjs-jquery-metadata{a} libjs-jquery-tablesorter{a} libjs-jquery-throttle-debounce{a} libk5crypto3{a} libkeyutils1{a} libkrb5-3{a} libkrb5support0{a} libmagic-mgc{a} libmagic1t64{a} libnsl2{a} libpipeline1{a} libpython3-stdlib{a} libpython3.12-minimal{a} libpython3.12-stdlib{a} libpython3.13-minimal{a} libpython3.13-stdlib{a} libreadline8t64{a} libtirpc-common{a} libtirpc3t64{a} libtool{a} libuchardet0{a} libunistring5{a} libxml2{a} m4{a} man-db{a} media-types{a} netbase{a} po-debconf{a} pybuild-plugin-pyproject{a} python3{a} python3-aiodns{a} python3-aiohappyeyeballs{a} python3-aiohttp{a} python3-aiosignal{a} python3-all{a} python3-aresponses{a} python3-async-generator{a} python3-async-timeout{a} python3-attr{a} python3-autocommand{a} python3-build{a} python3-cffi-backend{a} python3-coverage{a} python3-dateutil{a} python3-freezegun{a} python3-frozenlist{a} python3-idna{a} python3-inflect{a} python3-iniconfig{a} python3-installer{a} python3-jaraco.context{a} python3-jaraco.functools{a} python3-jaraco.text{a} python3-minimal{a} python3-more-itertools{a} python3-multidict{a} python3-packaging{a} python3-pkg-resources{a} python3-pluggy{a} python3-poetry-core{a} python3-pycares{a} python3-pyproject-hooks{a} python3-pytest{a} python3-pytest-asyncio{a} python3-pytest-cov{a} python3-pytest-freezer{a} python3-setuptools{a} python3-syrupy{a} python3-toml{a} python3-typeguard{a} python3-typing-extensions{a} python3-wheel{a} python3-yarl{a} python3-zipp{a} python3.12{a} python3.12-minimal{a} python3.13{a} python3.13-minimal{a} readline-common{a} sensible-utils{a} tzdata{a} The following packages are RECOMMENDED but will NOT be installed: ca-certificates curl javascript-common krb5-locales libarchive-cpio-perl libltdl-dev libmail-sendmail-perl lynx python3-pygments wget 0 packages upgraded, 109 newly installed, 0 to remove and 0 not upgraded. Need to get 36.3 MB of archives. After unpacking 154 MB will be used. Writing extended state information... Get: 1 http://deb.debian.org/debian unstable/main arm64 libpython3.13-minimal arm64 3.13.2-1 [853 kB] Get: 2 http://deb.debian.org/debian unstable/main arm64 libexpat1 arm64 2.6.4-1 [90.7 kB] Get: 3 http://deb.debian.org/debian unstable/main arm64 python3.13-minimal arm64 3.13.2-1 [1997 kB] Get: 4 http://deb.debian.org/debian unstable/main arm64 python3-minimal arm64 3.13.1-2 [27.0 kB] Get: 5 http://deb.debian.org/debian unstable/main arm64 media-types all 11.0.0 [27.6 kB] Get: 6 http://deb.debian.org/debian unstable/main arm64 netbase all 6.4 [12.8 kB] Get: 7 http://deb.debian.org/debian unstable/main arm64 tzdata all 2025a-2 [259 kB] Get: 8 http://deb.debian.org/debian unstable/main arm64 libffi8 arm64 3.4.7-1 [21.2 kB] Get: 9 http://deb.debian.org/debian unstable/main arm64 readline-common all 8.2-6 [69.4 kB] Get: 10 http://deb.debian.org/debian unstable/main arm64 libreadline8t64 arm64 8.2-6 [159 kB] Get: 11 http://deb.debian.org/debian unstable/main arm64 libpython3.13-stdlib arm64 3.13.2-1 [1914 kB] Get: 12 http://deb.debian.org/debian unstable/main arm64 python3.13 arm64 3.13.2-1 [745 kB] Get: 13 http://deb.debian.org/debian unstable/main arm64 libpython3-stdlib arm64 3.13.1-2 [9952 B] Get: 14 http://deb.debian.org/debian unstable/main arm64 python3 arm64 3.13.1-2 [28.0 kB] Get: 15 http://deb.debian.org/debian unstable/main arm64 libpython3.12-minimal arm64 3.12.9-1 [810 kB] Get: 16 http://deb.debian.org/debian unstable/main arm64 python3.12-minimal arm64 3.12.9-1 [1941 kB] Get: 17 http://deb.debian.org/debian unstable/main arm64 sensible-utils all 0.0.24 [24.8 kB] Get: 18 http://deb.debian.org/debian unstable/main arm64 libmagic-mgc arm64 1:5.45-3+b1 [314 kB] Get: 19 http://deb.debian.org/debian unstable/main arm64 libmagic1t64 arm64 1:5.45-3+b1 [102 kB] Get: 20 http://deb.debian.org/debian unstable/main arm64 file arm64 1:5.45-3+b1 [43.4 kB] Get: 21 http://deb.debian.org/debian unstable/main arm64 gettext-base arm64 0.23.1-1 [241 kB] Get: 22 http://deb.debian.org/debian unstable/main arm64 libuchardet0 arm64 0.0.8-1+b2 [69.2 kB] Get: 23 http://deb.debian.org/debian unstable/main arm64 groff-base arm64 1.23.0-7 [1129 kB] Get: 24 http://deb.debian.org/debian unstable/main arm64 bsdextrautils arm64 2.40.4-4 [92.1 kB] Get: 25 http://deb.debian.org/debian unstable/main arm64 libpipeline1 arm64 1.5.8-1 [40.2 kB] Get: 26 http://deb.debian.org/debian unstable/main arm64 man-db arm64 2.13.0-1 [1404 kB] Get: 27 http://deb.debian.org/debian unstable/main arm64 m4 arm64 1.4.19-5 [284 kB] Get: 28 http://deb.debian.org/debian unstable/main arm64 autoconf all 2.72-3 [493 kB] Get: 29 http://deb.debian.org/debian unstable/main arm64 autotools-dev all 20220109.1 [51.6 kB] Get: 30 http://deb.debian.org/debian unstable/main arm64 automake all 1:1.17-3 [862 kB] Get: 31 http://deb.debian.org/debian unstable/main arm64 autopoint all 0.23.1-1 [770 kB] Get: 32 http://deb.debian.org/debian unstable/main arm64 libdebhelper-perl all 13.24.1 [90.9 kB] Get: 33 http://deb.debian.org/debian unstable/main arm64 libtool all 2.5.4-3 [539 kB] Get: 34 http://deb.debian.org/debian unstable/main arm64 dh-autoreconf all 20 [17.1 kB] Get: 35 http://deb.debian.org/debian unstable/main arm64 libarchive-zip-perl all 1.68-1 [104 kB] Get: 36 http://deb.debian.org/debian unstable/main arm64 libfile-stripnondeterminism-perl all 1.14.1-2 [19.7 kB] Get: 37 http://deb.debian.org/debian unstable/main arm64 dh-strip-nondeterminism all 1.14.1-2 [8620 B] Get: 38 http://deb.debian.org/debian unstable/main arm64 libelf1t64 arm64 0.192-4 [189 kB] Get: 39 http://deb.debian.org/debian unstable/main arm64 dwz arm64 0.15-1+b1 [102 kB] Get: 40 http://deb.debian.org/debian unstable/main arm64 libunistring5 arm64 1.3-1 [449 kB] Get: 41 http://deb.debian.org/debian unstable/main arm64 libicu72 arm64 72.1-6 [9239 kB] Get: 42 http://deb.debian.org/debian unstable/main arm64 libxml2 arm64 2.12.7+dfsg+really2.9.14-0.2+b1 [630 kB] Get: 43 http://deb.debian.org/debian unstable/main arm64 gettext arm64 0.23.1-1 [1610 kB] Get: 44 http://deb.debian.org/debian unstable/main arm64 intltool-debian all 0.35.0+20060710.6 [22.9 kB] Get: 45 http://deb.debian.org/debian unstable/main arm64 po-debconf all 1.0.21+nmu1 [248 kB] Get: 46 http://deb.debian.org/debian unstable/main arm64 debhelper all 13.24.1 [920 kB] Get: 47 http://deb.debian.org/debian unstable/main arm64 python3-autocommand all 2.2.2-3 [13.6 kB] Get: 48 http://deb.debian.org/debian unstable/main arm64 python3-more-itertools all 10.6.0-1 [65.3 kB] Get: 49 http://deb.debian.org/debian unstable/main arm64 python3-typing-extensions all 4.12.2-2 [73.0 kB] Get: 50 http://deb.debian.org/debian unstable/main arm64 python3-typeguard all 4.4.1-1 [37.0 kB] Get: 51 http://deb.debian.org/debian unstable/main arm64 python3-inflect all 7.3.1-2 [32.4 kB] Get: 52 http://deb.debian.org/debian unstable/main arm64 python3-jaraco.context all 6.0.0-1 [7984 B] Get: 53 http://deb.debian.org/debian unstable/main arm64 python3-jaraco.functools all 4.1.0-1 [12.0 kB] Get: 54 http://deb.debian.org/debian unstable/main arm64 python3-pkg-resources all 75.8.0-1 [222 kB] Get: 55 http://deb.debian.org/debian unstable/main arm64 python3-jaraco.text all 4.0.0-1 [11.4 kB] Get: 56 http://deb.debian.org/debian unstable/main arm64 python3-zipp all 3.21.0-1 [10.6 kB] Get: 57 http://deb.debian.org/debian unstable/main arm64 python3-setuptools all 75.8.0-1 [724 kB] Get: 58 http://deb.debian.org/debian unstable/main arm64 dh-python all 6.20250108 [113 kB] Get: 59 http://deb.debian.org/debian unstable/main arm64 libcares2 arm64 1.34.4-2.1 [92.2 kB] Get: 60 http://deb.debian.org/debian unstable/main arm64 libcom-err2 arm64 1.47.2-1 [23.9 kB] Get: 61 http://deb.debian.org/debian unstable/main arm64 libkrb5support0 arm64 1.21.3-4 [32.2 kB] Get: 62 http://deb.debian.org/debian unstable/main arm64 libk5crypto3 arm64 1.21.3-4 [81.5 kB] Get: 63 http://deb.debian.org/debian unstable/main arm64 libkeyutils1 arm64 1.6.3-4 [9352 B] Get: 64 http://deb.debian.org/debian unstable/main arm64 libkrb5-3 arm64 1.21.3-4 [308 kB] Get: 65 http://deb.debian.org/debian unstable/main arm64 libgssapi-krb5-2 arm64 1.21.3-4 [127 kB] Get: 66 http://deb.debian.org/debian unstable/main arm64 libjs-jquery all 3.6.1+dfsg+~3.5.14-1 [326 kB] Get: 67 http://deb.debian.org/debian unstable/main arm64 libjs-jquery-hotkeys all 0.2.0-1 [12.6 kB] Get: 68 http://deb.debian.org/debian unstable/main arm64 libjs-jquery-isonscreen all 1.2.0-1.1 [3196 B] Get: 69 http://deb.debian.org/debian unstable/main arm64 libjs-jquery-metadata all 12-4 [6532 B] Get: 70 http://deb.debian.org/debian unstable/main arm64 libjs-jquery-tablesorter all 1:2.31.3+dfsg1-4 [184 kB] Get: 71 http://deb.debian.org/debian unstable/main arm64 libjs-jquery-throttle-debounce all 1.1+dfsg.1-2 [12.2 kB] Get: 72 http://deb.debian.org/debian unstable/main arm64 libtirpc-common all 1.3.4+ds-1.3 [10.9 kB] Get: 73 http://deb.debian.org/debian unstable/main arm64 libtirpc3t64 arm64 1.3.4+ds-1.3+b1 [78.7 kB] Get: 74 http://deb.debian.org/debian unstable/main arm64 libnsl2 arm64 1.3.0-3+b3 [37.9 kB] Get: 75 http://deb.debian.org/debian unstable/main arm64 libpython3.12-stdlib arm64 3.12.9-1 [1909 kB] Get: 76 http://deb.debian.org/debian unstable/main arm64 python3-packaging all 24.2-1 [55.3 kB] Get: 77 http://deb.debian.org/debian unstable/main arm64 python3-pyproject-hooks all 1.2.0-1 [11.7 kB] Get: 78 http://deb.debian.org/debian unstable/main arm64 python3-toml all 0.10.2-1 [16.2 kB] Get: 79 http://deb.debian.org/debian unstable/main arm64 python3-wheel all 0.45.1-1 [56.7 kB] Get: 80 http://deb.debian.org/debian unstable/main arm64 python3-build all 1.2.2-1 [36.0 kB] Get: 81 http://deb.debian.org/debian unstable/main arm64 python3-installer all 0.7.0+dfsg1-3 [18.6 kB] Get: 82 http://deb.debian.org/debian unstable/main arm64 pybuild-plugin-pyproject all 6.20250108 [11.6 kB] Get: 83 http://deb.debian.org/debian unstable/main arm64 python3-idna all 3.10-1 [42.0 kB] Get: 84 http://deb.debian.org/debian unstable/main arm64 python3-cffi-backend arm64 1.17.1-2+b1 [94.8 kB] Get: 85 http://deb.debian.org/debian unstable/main arm64 python3-pycares arm64 4.4.0-2+b3 [30.6 kB] Get: 86 http://deb.debian.org/debian unstable/main arm64 python3-aiodns all 3.2.0-2 [8216 B] Get: 87 http://deb.debian.org/debian unstable/main arm64 python3-aiohappyeyeballs all 2.4.6-1 [13.5 kB] Get: 88 http://deb.debian.org/debian unstable/main arm64 python3-multidict arm64 6.1.0-1+b1 [37.8 kB] Get: 89 http://deb.debian.org/debian unstable/main arm64 python3-yarl arm64 1.13.1-1+b1 [106 kB] Get: 90 http://deb.debian.org/debian unstable/main arm64 python3-async-timeout all 5.0.1-1 [8324 B] Get: 91 http://deb.debian.org/debian unstable/main arm64 python3-frozenlist arm64 1.5.0-1+b2 [53.8 kB] Get: 92 http://deb.debian.org/debian unstable/main arm64 python3-aiosignal all 1.3.2-1 [6100 B] Get: 93 http://deb.debian.org/debian unstable/main arm64 python3-attr all 25.1.0-1 [68.7 kB] Get: 94 http://deb.debian.org/debian unstable/main arm64 python3-aiohttp arm64 3.10.11-1 [335 kB] Get: 95 http://deb.debian.org/debian unstable/main arm64 python3.12 arm64 3.12.9-1 [681 kB] Get: 96 http://deb.debian.org/debian unstable/main arm64 python3-all arm64 3.13.1-2 [1056 B] Get: 97 http://deb.debian.org/debian unstable/main arm64 python3-async-generator all 1.10-4 [17.4 kB] Get: 98 http://deb.debian.org/debian unstable/main arm64 python3-iniconfig all 1.1.1-2 [6396 B] Get: 99 http://deb.debian.org/debian unstable/main arm64 python3-pluggy all 1.5.0-1 [26.9 kB] Get: 100 http://deb.debian.org/debian unstable/main arm64 python3-pytest all 8.3.4-1 [250 kB] Get: 101 http://deb.debian.org/debian unstable/main arm64 python3-pytest-asyncio all 0.25.1-1 [16.7 kB] Get: 102 http://deb.debian.org/debian unstable/main arm64 python3-aresponses all 3.0.0-2 [13.5 kB] Get: 103 http://deb.debian.org/debian unstable/main arm64 python3-coverage arm64 7.6.0+dfsg1-2+b1 [177 kB] Get: 104 http://deb.debian.org/debian unstable/main arm64 python3-dateutil all 2.9.0-3 [79.3 kB] Get: 105 http://deb.debian.org/debian unstable/main arm64 python3-freezegun all 1.5.1-1.2 [17.2 kB] Get: 106 http://deb.debian.org/debian unstable/main arm64 python3-poetry-core all 2.0.1-2 [225 kB] Get: 107 http://deb.debian.org/debian unstable/main arm64 python3-pytest-cov all 5.0.0-1 [26.8 kB] Get: 108 http://deb.debian.org/debian unstable/main arm64 python3-pytest-freezer all 0.4.9-1 [4544 B] Get: 109 http://deb.debian.org/debian unstable/main arm64 python3-syrupy all 4.8.1-1 [54.2 kB] Fetched 36.3 MB in 0s (75.6 MB/s) Preconfiguring packages ... Selecting previously unselected package libpython3.13-minimal:arm64. (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 ... 19889 files and directories currently installed.) Preparing to unpack .../libpython3.13-minimal_3.13.2-1_arm64.deb ... Unpacking libpython3.13-minimal:arm64 (3.13.2-1) ... Selecting previously unselected package libexpat1:arm64. Preparing to unpack .../libexpat1_2.6.4-1_arm64.deb ... Unpacking libexpat1:arm64 (2.6.4-1) ... Selecting previously unselected package python3.13-minimal. Preparing to unpack .../python3.13-minimal_3.13.2-1_arm64.deb ... Unpacking python3.13-minimal (3.13.2-1) ... Setting up libpython3.13-minimal:arm64 (3.13.2-1) ... Setting up libexpat1:arm64 (2.6.4-1) ... Setting up python3.13-minimal (3.13.2-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 ... 20223 files and directories currently installed.) Preparing to unpack .../0-python3-minimal_3.13.1-2_arm64.deb ... Unpacking python3-minimal (3.13.1-2) ... Selecting previously unselected package media-types. Preparing to unpack .../1-media-types_11.0.0_all.deb ... Unpacking media-types (11.0.0) ... Selecting previously unselected package netbase. Preparing to unpack .../2-netbase_6.4_all.deb ... Unpacking netbase (6.4) ... Selecting previously unselected package tzdata. Preparing to unpack .../3-tzdata_2025a-2_all.deb ... Unpacking tzdata (2025a-2) ... Selecting previously unselected package libffi8:arm64. Preparing to unpack .../4-libffi8_3.4.7-1_arm64.deb ... Unpacking libffi8:arm64 (3.4.7-1) ... Selecting previously unselected package readline-common. Preparing to unpack .../5-readline-common_8.2-6_all.deb ... Unpacking readline-common (8.2-6) ... Selecting previously unselected package libreadline8t64:arm64. Preparing to unpack .../6-libreadline8t64_8.2-6_arm64.deb ... Adding 'diversion of /lib/aarch64-linux-gnu/libhistory.so.8 to /lib/aarch64-linux-gnu/libhistory.so.8.usr-is-merged by libreadline8t64' Adding 'diversion of /lib/aarch64-linux-gnu/libhistory.so.8.2 to /lib/aarch64-linux-gnu/libhistory.so.8.2.usr-is-merged by libreadline8t64' Adding 'diversion of /lib/aarch64-linux-gnu/libreadline.so.8 to /lib/aarch64-linux-gnu/libreadline.so.8.usr-is-merged by libreadline8t64' Adding 'diversion of /lib/aarch64-linux-gnu/libreadline.so.8.2 to /lib/aarch64-linux-gnu/libreadline.so.8.2.usr-is-merged by libreadline8t64' Unpacking libreadline8t64:arm64 (8.2-6) ... Selecting previously unselected package libpython3.13-stdlib:arm64. Preparing to unpack .../7-libpython3.13-stdlib_3.13.2-1_arm64.deb ... Unpacking libpython3.13-stdlib:arm64 (3.13.2-1) ... Selecting previously unselected package python3.13. Preparing to unpack .../8-python3.13_3.13.2-1_arm64.deb ... Unpacking python3.13 (3.13.2-1) ... Selecting previously unselected package libpython3-stdlib:arm64. Preparing to unpack .../9-libpython3-stdlib_3.13.1-2_arm64.deb ... Unpacking libpython3-stdlib:arm64 (3.13.1-2) ... Setting up python3-minimal (3.13.1-2) ... Selecting previously unselected package python3. (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 21233 files and directories currently installed.) Preparing to unpack .../00-python3_3.13.1-2_arm64.deb ... Unpacking python3 (3.13.1-2) ... Selecting previously unselected package libpython3.12-minimal:arm64. Preparing to unpack .../01-libpython3.12-minimal_3.12.9-1_arm64.deb ... Unpacking libpython3.12-minimal:arm64 (3.12.9-1) ... Selecting previously unselected package python3.12-minimal. Preparing to unpack .../02-python3.12-minimal_3.12.9-1_arm64.deb ... Unpacking python3.12-minimal (3.12.9-1) ... Selecting previously unselected package sensible-utils. Preparing to unpack .../03-sensible-utils_0.0.24_all.deb ... Unpacking sensible-utils (0.0.24) ... Selecting previously unselected package libmagic-mgc. Preparing to unpack .../04-libmagic-mgc_1%3a5.45-3+b1_arm64.deb ... Unpacking libmagic-mgc (1:5.45-3+b1) ... Selecting previously unselected package libmagic1t64:arm64. Preparing to unpack .../05-libmagic1t64_1%3a5.45-3+b1_arm64.deb ... Unpacking libmagic1t64:arm64 (1:5.45-3+b1) ... Selecting previously unselected package file. Preparing to unpack .../06-file_1%3a5.45-3+b1_arm64.deb ... Unpacking file (1:5.45-3+b1) ... Selecting previously unselected package gettext-base. Preparing to unpack .../07-gettext-base_0.23.1-1_arm64.deb ... Unpacking gettext-base (0.23.1-1) ... Selecting previously unselected package libuchardet0:arm64. Preparing to unpack .../08-libuchardet0_0.0.8-1+b2_arm64.deb ... Unpacking libuchardet0:arm64 (0.0.8-1+b2) ... Selecting previously unselected package groff-base. Preparing to unpack .../09-groff-base_1.23.0-7_arm64.deb ... Unpacking groff-base (1.23.0-7) ... Selecting previously unselected package bsdextrautils. Preparing to unpack .../10-bsdextrautils_2.40.4-4_arm64.deb ... Unpacking bsdextrautils (2.40.4-4) ... Selecting previously unselected package libpipeline1:arm64. Preparing to unpack .../11-libpipeline1_1.5.8-1_arm64.deb ... Unpacking libpipeline1:arm64 (1.5.8-1) ... Selecting previously unselected package man-db. Preparing to unpack .../12-man-db_2.13.0-1_arm64.deb ... Unpacking man-db (2.13.0-1) ... Selecting previously unselected package m4. Preparing to unpack .../13-m4_1.4.19-5_arm64.deb ... Unpacking m4 (1.4.19-5) ... Selecting previously unselected package autoconf. Preparing to unpack .../14-autoconf_2.72-3_all.deb ... Unpacking autoconf (2.72-3) ... Selecting previously unselected package autotools-dev. Preparing to unpack .../15-autotools-dev_20220109.1_all.deb ... Unpacking autotools-dev (20220109.1) ... Selecting previously unselected package automake. Preparing to unpack .../16-automake_1%3a1.17-3_all.deb ... Unpacking automake (1:1.17-3) ... Selecting previously unselected package autopoint. Preparing to unpack .../17-autopoint_0.23.1-1_all.deb ... Unpacking autopoint (0.23.1-1) ... Selecting previously unselected package libdebhelper-perl. Preparing to unpack .../18-libdebhelper-perl_13.24.1_all.deb ... Unpacking libdebhelper-perl (13.24.1) ... Selecting previously unselected package libtool. Preparing to unpack .../19-libtool_2.5.4-3_all.deb ... Unpacking libtool (2.5.4-3) ... Selecting previously unselected package dh-autoreconf. Preparing to unpack .../20-dh-autoreconf_20_all.deb ... Unpacking dh-autoreconf (20) ... Selecting previously unselected package libarchive-zip-perl. Preparing to unpack .../21-libarchive-zip-perl_1.68-1_all.deb ... Unpacking libarchive-zip-perl (1.68-1) ... Selecting previously unselected package libfile-stripnondeterminism-perl. Preparing to unpack .../22-libfile-stripnondeterminism-perl_1.14.1-2_all.deb ... Unpacking libfile-stripnondeterminism-perl (1.14.1-2) ... Selecting previously unselected package dh-strip-nondeterminism. Preparing to unpack .../23-dh-strip-nondeterminism_1.14.1-2_all.deb ... Unpacking dh-strip-nondeterminism (1.14.1-2) ... Selecting previously unselected package libelf1t64:arm64. Preparing to unpack .../24-libelf1t64_0.192-4_arm64.deb ... Unpacking libelf1t64:arm64 (0.192-4) ... Selecting previously unselected package dwz. Preparing to unpack .../25-dwz_0.15-1+b1_arm64.deb ... Unpacking dwz (0.15-1+b1) ... Selecting previously unselected package libunistring5:arm64. Preparing to unpack .../26-libunistring5_1.3-1_arm64.deb ... Unpacking libunistring5:arm64 (1.3-1) ... Selecting previously unselected package libicu72:arm64. Preparing to unpack .../27-libicu72_72.1-6_arm64.deb ... Unpacking libicu72:arm64 (72.1-6) ... Selecting previously unselected package libxml2:arm64. Preparing to unpack .../28-libxml2_2.12.7+dfsg+really2.9.14-0.2+b1_arm64.deb ... Unpacking libxml2:arm64 (2.12.7+dfsg+really2.9.14-0.2+b1) ... Selecting previously unselected package gettext. Preparing to unpack .../29-gettext_0.23.1-1_arm64.deb ... Unpacking gettext (0.23.1-1) ... Selecting previously unselected package intltool-debian. Preparing to unpack .../30-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 .../31-po-debconf_1.0.21+nmu1_all.deb ... Unpacking po-debconf (1.0.21+nmu1) ... Selecting previously unselected package debhelper. Preparing to unpack .../32-debhelper_13.24.1_all.deb ... Unpacking debhelper (13.24.1) ... Selecting previously unselected package python3-autocommand. Preparing to unpack .../33-python3-autocommand_2.2.2-3_all.deb ... Unpacking python3-autocommand (2.2.2-3) ... Selecting previously unselected package python3-more-itertools. Preparing to unpack .../34-python3-more-itertools_10.6.0-1_all.deb ... Unpacking python3-more-itertools (10.6.0-1) ... Selecting previously unselected package python3-typing-extensions. Preparing to unpack .../35-python3-typing-extensions_4.12.2-2_all.deb ... Unpacking python3-typing-extensions (4.12.2-2) ... Selecting previously unselected package python3-typeguard. Preparing to unpack .../36-python3-typeguard_4.4.1-1_all.deb ... Unpacking python3-typeguard (4.4.1-1) ... Selecting previously unselected package python3-inflect. Preparing to unpack .../37-python3-inflect_7.3.1-2_all.deb ... Unpacking python3-inflect (7.3.1-2) ... Selecting previously unselected package python3-jaraco.context. Preparing to unpack .../38-python3-jaraco.context_6.0.0-1_all.deb ... Unpacking python3-jaraco.context (6.0.0-1) ... Selecting previously unselected package python3-jaraco.functools. Preparing to unpack .../39-python3-jaraco.functools_4.1.0-1_all.deb ... Unpacking python3-jaraco.functools (4.1.0-1) ... Selecting previously unselected package python3-pkg-resources. Preparing to unpack .../40-python3-pkg-resources_75.8.0-1_all.deb ... Unpacking python3-pkg-resources (75.8.0-1) ... Selecting previously unselected package python3-jaraco.text. Preparing to unpack .../41-python3-jaraco.text_4.0.0-1_all.deb ... Unpacking python3-jaraco.text (4.0.0-1) ... Selecting previously unselected package python3-zipp. Preparing to unpack .../42-python3-zipp_3.21.0-1_all.deb ... Unpacking python3-zipp (3.21.0-1) ... Selecting previously unselected package python3-setuptools. Preparing to unpack .../43-python3-setuptools_75.8.0-1_all.deb ... Unpacking python3-setuptools (75.8.0-1) ... Selecting previously unselected package dh-python. Preparing to unpack .../44-dh-python_6.20250108_all.deb ... Unpacking dh-python (6.20250108) ... Selecting previously unselected package libcares2:arm64. Preparing to unpack .../45-libcares2_1.34.4-2.1_arm64.deb ... Unpacking libcares2:arm64 (1.34.4-2.1) ... Selecting previously unselected package libcom-err2:arm64. Preparing to unpack .../46-libcom-err2_1.47.2-1_arm64.deb ... Unpacking libcom-err2:arm64 (1.47.2-1) ... Selecting previously unselected package libkrb5support0:arm64. Preparing to unpack .../47-libkrb5support0_1.21.3-4_arm64.deb ... Unpacking libkrb5support0:arm64 (1.21.3-4) ... Selecting previously unselected package libk5crypto3:arm64. Preparing to unpack .../48-libk5crypto3_1.21.3-4_arm64.deb ... Unpacking libk5crypto3:arm64 (1.21.3-4) ... Selecting previously unselected package libkeyutils1:arm64. Preparing to unpack .../49-libkeyutils1_1.6.3-4_arm64.deb ... Unpacking libkeyutils1:arm64 (1.6.3-4) ... Selecting previously unselected package libkrb5-3:arm64. Preparing to unpack .../50-libkrb5-3_1.21.3-4_arm64.deb ... Unpacking libkrb5-3:arm64 (1.21.3-4) ... Selecting previously unselected package libgssapi-krb5-2:arm64. Preparing to unpack .../51-libgssapi-krb5-2_1.21.3-4_arm64.deb ... Unpacking libgssapi-krb5-2:arm64 (1.21.3-4) ... Selecting previously unselected package libjs-jquery. Preparing to unpack .../52-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 libjs-jquery-hotkeys. Preparing to unpack .../53-libjs-jquery-hotkeys_0.2.0-1_all.deb ... Unpacking libjs-jquery-hotkeys (0.2.0-1) ... Selecting previously unselected package libjs-jquery-isonscreen. Preparing to unpack .../54-libjs-jquery-isonscreen_1.2.0-1.1_all.deb ... Unpacking libjs-jquery-isonscreen (1.2.0-1.1) ... Selecting previously unselected package libjs-jquery-metadata. Preparing to unpack .../55-libjs-jquery-metadata_12-4_all.deb ... Unpacking libjs-jquery-metadata (12-4) ... Selecting previously unselected package libjs-jquery-tablesorter. Preparing to unpack .../56-libjs-jquery-tablesorter_1%3a2.31.3+dfsg1-4_all.deb ... Unpacking libjs-jquery-tablesorter (1:2.31.3+dfsg1-4) ... Selecting previously unselected package libjs-jquery-throttle-debounce. Preparing to unpack .../57-libjs-jquery-throttle-debounce_1.1+dfsg.1-2_all.deb ... Unpacking libjs-jquery-throttle-debounce (1.1+dfsg.1-2) ... Selecting previously unselected package libtirpc-common. Preparing to unpack .../58-libtirpc-common_1.3.4+ds-1.3_all.deb ... Unpacking libtirpc-common (1.3.4+ds-1.3) ... Selecting previously unselected package libtirpc3t64:arm64. Preparing to unpack .../59-libtirpc3t64_1.3.4+ds-1.3+b1_arm64.deb ... Adding 'diversion of /lib/aarch64-linux-gnu/libtirpc.so.3 to /lib/aarch64-linux-gnu/libtirpc.so.3.usr-is-merged by libtirpc3t64' Adding 'diversion of /lib/aarch64-linux-gnu/libtirpc.so.3.0.0 to /lib/aarch64-linux-gnu/libtirpc.so.3.0.0.usr-is-merged by libtirpc3t64' Unpacking libtirpc3t64:arm64 (1.3.4+ds-1.3+b1) ... Selecting previously unselected package libnsl2:arm64. Preparing to unpack .../60-libnsl2_1.3.0-3+b3_arm64.deb ... Unpacking libnsl2:arm64 (1.3.0-3+b3) ... Selecting previously unselected package libpython3.12-stdlib:arm64. Preparing to unpack .../61-libpython3.12-stdlib_3.12.9-1_arm64.deb ... Unpacking libpython3.12-stdlib:arm64 (3.12.9-1) ... Selecting previously unselected package python3-packaging. Preparing to unpack .../62-python3-packaging_24.2-1_all.deb ... Unpacking python3-packaging (24.2-1) ... Selecting previously unselected package python3-pyproject-hooks. Preparing to unpack .../63-python3-pyproject-hooks_1.2.0-1_all.deb ... Unpacking python3-pyproject-hooks (1.2.0-1) ... Selecting previously unselected package python3-toml. Preparing to unpack .../64-python3-toml_0.10.2-1_all.deb ... Unpacking python3-toml (0.10.2-1) ... Selecting previously unselected package python3-wheel. Preparing to unpack .../65-python3-wheel_0.45.1-1_all.deb ... Unpacking python3-wheel (0.45.1-1) ... Selecting previously unselected package python3-build. Preparing to unpack .../66-python3-build_1.2.2-1_all.deb ... Unpacking python3-build (1.2.2-1) ... Selecting previously unselected package python3-installer. Preparing to unpack .../67-python3-installer_0.7.0+dfsg1-3_all.deb ... Unpacking python3-installer (0.7.0+dfsg1-3) ... Selecting previously unselected package pybuild-plugin-pyproject. Preparing to unpack .../68-pybuild-plugin-pyproject_6.20250108_all.deb ... Unpacking pybuild-plugin-pyproject (6.20250108) ... Selecting previously unselected package python3-idna. Preparing to unpack .../69-python3-idna_3.10-1_all.deb ... Unpacking python3-idna (3.10-1) ... Selecting previously unselected package python3-cffi-backend:arm64. Preparing to unpack .../70-python3-cffi-backend_1.17.1-2+b1_arm64.deb ... Unpacking python3-cffi-backend:arm64 (1.17.1-2+b1) ... Selecting previously unselected package python3-pycares. Preparing to unpack .../71-python3-pycares_4.4.0-2+b3_arm64.deb ... Unpacking python3-pycares (4.4.0-2+b3) ... Selecting previously unselected package python3-aiodns. Preparing to unpack .../72-python3-aiodns_3.2.0-2_all.deb ... Unpacking python3-aiodns (3.2.0-2) ... Selecting previously unselected package python3-aiohappyeyeballs. Preparing to unpack .../73-python3-aiohappyeyeballs_2.4.6-1_all.deb ... Unpacking python3-aiohappyeyeballs (2.4.6-1) ... Selecting previously unselected package python3-multidict. Preparing to unpack .../74-python3-multidict_6.1.0-1+b1_arm64.deb ... Unpacking python3-multidict (6.1.0-1+b1) ... Selecting previously unselected package python3-yarl. Preparing to unpack .../75-python3-yarl_1.13.1-1+b1_arm64.deb ... Unpacking python3-yarl (1.13.1-1+b1) ... Selecting previously unselected package python3-async-timeout. Preparing to unpack .../76-python3-async-timeout_5.0.1-1_all.deb ... Unpacking python3-async-timeout (5.0.1-1) ... Selecting previously unselected package python3-frozenlist. Preparing to unpack .../77-python3-frozenlist_1.5.0-1+b2_arm64.deb ... Unpacking python3-frozenlist (1.5.0-1+b2) ... Selecting previously unselected package python3-aiosignal. Preparing to unpack .../78-python3-aiosignal_1.3.2-1_all.deb ... Unpacking python3-aiosignal (1.3.2-1) ... Selecting previously unselected package python3-attr. Preparing to unpack .../79-python3-attr_25.1.0-1_all.deb ... Unpacking python3-attr (25.1.0-1) ... Selecting previously unselected package python3-aiohttp. Preparing to unpack .../80-python3-aiohttp_3.10.11-1_arm64.deb ... Unpacking python3-aiohttp (3.10.11-1) ... Selecting previously unselected package python3.12. Preparing to unpack .../81-python3.12_3.12.9-1_arm64.deb ... Unpacking python3.12 (3.12.9-1) ... Selecting previously unselected package python3-all. Preparing to unpack .../82-python3-all_3.13.1-2_arm64.deb ... Unpacking python3-all (3.13.1-2) ... Selecting previously unselected package python3-async-generator. Preparing to unpack .../83-python3-async-generator_1.10-4_all.deb ... Unpacking python3-async-generator (1.10-4) ... Selecting previously unselected package python3-iniconfig. Preparing to unpack .../84-python3-iniconfig_1.1.1-2_all.deb ... Unpacking python3-iniconfig (1.1.1-2) ... Selecting previously unselected package python3-pluggy. Preparing to unpack .../85-python3-pluggy_1.5.0-1_all.deb ... Unpacking python3-pluggy (1.5.0-1) ... Selecting previously unselected package python3-pytest. Preparing to unpack .../86-python3-pytest_8.3.4-1_all.deb ... Unpacking python3-pytest (8.3.4-1) ... Selecting previously unselected package python3-pytest-asyncio. Preparing to unpack .../87-python3-pytest-asyncio_0.25.1-1_all.deb ... Unpacking python3-pytest-asyncio (0.25.1-1) ... Selecting previously unselected package python3-aresponses. Preparing to unpack .../88-python3-aresponses_3.0.0-2_all.deb ... Unpacking python3-aresponses (3.0.0-2) ... Selecting previously unselected package python3-coverage. Preparing to unpack .../89-python3-coverage_7.6.0+dfsg1-2+b1_arm64.deb ... Unpacking python3-coverage (7.6.0+dfsg1-2+b1) ... Selecting previously unselected package python3-dateutil. Preparing to unpack .../90-python3-dateutil_2.9.0-3_all.deb ... Unpacking python3-dateutil (2.9.0-3) ... Selecting previously unselected package python3-freezegun. Preparing to unpack .../91-python3-freezegun_1.5.1-1.2_all.deb ... Unpacking python3-freezegun (1.5.1-1.2) ... Selecting previously unselected package python3-poetry-core. Preparing to unpack .../92-python3-poetry-core_2.0.1-2_all.deb ... Unpacking python3-poetry-core (2.0.1-2) ... Selecting previously unselected package python3-pytest-cov. Preparing to unpack .../93-python3-pytest-cov_5.0.0-1_all.deb ... Unpacking python3-pytest-cov (5.0.0-1) ... Selecting previously unselected package python3-pytest-freezer. Preparing to unpack .../94-python3-pytest-freezer_0.4.9-1_all.deb ... Unpacking python3-pytest-freezer (0.4.9-1) ... Selecting previously unselected package python3-syrupy. Preparing to unpack .../95-python3-syrupy_4.8.1-1_all.deb ... Unpacking python3-syrupy (4.8.1-1) ... Setting up media-types (11.0.0) ... Setting up libpipeline1:arm64 (1.5.8-1) ... Setting up libkeyutils1:arm64 (1.6.3-4) ... Setting up libicu72:arm64 (72.1-6) ... Setting up bsdextrautils (2.40.4-4) ... Setting up libmagic-mgc (1:5.45-3+b1) ... Setting up libarchive-zip-perl (1.68-1) ... Setting up libtirpc-common (1.3.4+ds-1.3) ... Setting up libdebhelper-perl (13.24.1) ... Setting up libmagic1t64:arm64 (1:5.45-3+b1) ... Setting up libpython3.12-minimal:arm64 (3.12.9-1) ... Setting up gettext-base (0.23.1-1) ... Setting up m4 (1.4.19-5) ... Setting up libcom-err2:arm64 (1.47.2-1) ... Setting up file (1:5.45-3+b1) ... Setting up libjs-jquery-throttle-debounce (1.1+dfsg.1-2) ... Setting up libelf1t64:arm64 (0.192-4) ... Setting up libkrb5support0:arm64 (1.21.3-4) ... Setting up tzdata (2025a-2) ... Current default time zone: 'Etc/UTC' Local time is now: Fri Mar 27 05:28:48 UTC 2026. Universal Time is now: Fri Mar 27 05:28:48 UTC 2026. Run 'dpkg-reconfigure tzdata' if you wish to change it. Setting up autotools-dev (20220109.1) ... Setting up libunistring5:arm64 (1.3-1) ... Setting up libcares2:arm64 (1.34.4-2.1) ... Setting up autopoint (0.23.1-1) ... Setting up libk5crypto3:arm64 (1.21.3-4) ... Setting up autoconf (2.72-3) ... Setting up libffi8:arm64 (3.4.7-1) ... Setting up dwz (0.15-1+b1) ... Setting up sensible-utils (0.0.24) ... Setting up libuchardet0:arm64 (0.0.8-1+b2) ... Setting up netbase (6.4) ... Setting up libkrb5-3:arm64 (1.21.3-4) ... Setting up libjs-jquery (3.6.1+dfsg+~3.5.14-1) ... Setting up libjs-jquery-hotkeys (0.2.0-1) ... Setting up readline-common (8.2-6) ... Setting up libxml2:arm64 (2.12.7+dfsg+really2.9.14-0.2+b1) ... Setting up automake (1:1.17-3) ... update-alternatives: using /usr/bin/automake-1.17 to provide /usr/bin/automake (automake) in auto mode Setting up libfile-stripnondeterminism-perl (1.14.1-2) ... Setting up python3.12-minimal (3.12.9-1) ... Setting up gettext (0.23.1-1) ... Setting up libtool (2.5.4-3) ... Setting up intltool-debian (0.35.0+20060710.6) ... Setting up dh-autoreconf (20) ... Setting up libjs-jquery-metadata (12-4) ... Setting up libjs-jquery-isonscreen (1.2.0-1.1) ... Setting up libgssapi-krb5-2:arm64 (1.21.3-4) ... Setting up libreadline8t64:arm64 (8.2-6) ... Setting up dh-strip-nondeterminism (1.14.1-2) ... Setting up libjs-jquery-tablesorter (1:2.31.3+dfsg1-4) ... Setting up groff-base (1.23.0-7) ... Setting up libpython3.13-stdlib:arm64 (3.13.2-1) ... Setting up libpython3-stdlib:arm64 (3.13.1-2) ... Setting up libtirpc3t64:arm64 (1.3.4+ds-1.3+b1) ... Setting up python3.13 (3.13.2-1) ... Setting up po-debconf (1.0.21+nmu1) ... Setting up python3 (3.13.1-2) ... Setting up python3-zipp (3.21.0-1) ... Setting up python3-autocommand (2.2.2-3) ... Setting up man-db (2.13.0-1) ... Not building database; man-db/auto-update is not 'true'. Setting up python3-wheel (0.45.1-1) ... Setting up python3-multidict (6.1.0-1+b1) ... Setting up python3-frozenlist (1.5.0-1+b2) ... Setting up python3-aiosignal (1.3.2-1) ... Setting up python3-async-timeout (5.0.1-1) ... Setting up python3-packaging (24.2-1) ... Setting up python3-pyproject-hooks (1.2.0-1) ... Setting up python3-poetry-core (2.0.1-2) ... Setting up python3-idna (3.10-1) ... Setting up python3-typing-extensions (4.12.2-2) ... Setting up python3-aiohappyeyeballs (2.4.6-1) ... Setting up python3-toml (0.10.2-1) ... Setting up python3-installer (0.7.0+dfsg1-3) ... Setting up python3-pluggy (1.5.0-1) ... Setting up libnsl2:arm64 (1.3.0-3+b3) ... Setting up python3-dateutil (2.9.0-3) ... Setting up python3-build (1.2.2-1) ... Setting up python3-yarl (1.13.1-1+b1) ... Setting up python3-freezegun (1.5.1-1.2) ... Setting up python3-cffi-backend:arm64 (1.17.1-2+b1) ... Setting up python3-more-itertools (10.6.0-1) ... Setting up python3-iniconfig (1.1.1-2) ... Setting up python3-attr (25.1.0-1) ... Setting up python3-jaraco.functools (4.1.0-1) ... Setting up libpython3.12-stdlib:arm64 (3.12.9-1) ... Setting up python3-async-generator (1.10-4) ... Setting up python3-jaraco.context (6.0.0-1) ... Setting up python3-pytest (8.3.4-1) ... Setting up python3-syrupy (4.8.1-1) ... Setting up python3.12 (3.12.9-1) ... Setting up python3-typeguard (4.4.1-1) ... Setting up python3-aiohttp (3.10.11-1) ... Setting up python3-all (3.13.1-2) ... Setting up python3-coverage (7.6.0+dfsg1-2+b1) ... Setting up debhelper (13.24.1) ... Setting up python3-pytest-cov (5.0.0-1) ... Setting up python3-inflect (7.3.1-2) ... Setting up python3-jaraco.text (4.0.0-1) ... Setting up python3-pytest-freezer (0.4.9-1) ... Setting up python3-pytest-asyncio (0.25.1-1) ... Setting up python3-pycares (4.4.0-2+b3) ... Setting up python3-pkg-resources (75.8.0-1) ... Setting up python3-setuptools (75.8.0-1) ... Setting up python3-aresponses (3.0.0-2) ... Setting up python3-aiodns (3.2.0-2) ... Setting up dh-python (6.20250108) ... Setting up pybuild-plugin-pyproject (6.20250108) ... Processing triggers for libc-bin (2.40-7) ... 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/forecast-solar-4.0.0/ && 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 > ../forecast-solar_4.0.0-1_source.changes dpkg-buildpackage: info: source package forecast-solar dpkg-buildpackage: info: source version 4.0.0-1 dpkg-buildpackage: info: source distribution unstable dpkg-buildpackage: info: source changed by Edward Betts dpkg-source --before-build . dpkg-buildpackage: info: host architecture arm64 debian/rules clean dh clean --buildsystem=pybuild dh_auto_clean -O--buildsystem=pybuild dh_autoreconf_clean -O--buildsystem=pybuild dh_clean -O--buildsystem=pybuild 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 dh_auto_build -O--buildsystem=pybuild I: pybuild plugin_pyproject:129: Building wheel for python3.12 with "build" module I: pybuild base:311: python3.12 -m build --skip-dependency-check --no-isolation --wheel --outdir /build/reproducible-path/forecast-solar-4.0.0/.pybuild/cpython3_3.12 * Building wheel... Successfully built forecast_solar-0.0.0-py3-none-any.whl I: pybuild plugin_pyproject:144: Unpacking wheel built for python3.12 with "installer" module I: pybuild plugin_pyproject:129: Building wheel for python3.13 with "build" module I: pybuild base:311: python3.13 -m build --skip-dependency-check --no-isolation --wheel --outdir /build/reproducible-path/forecast-solar-4.0.0/.pybuild/cpython3_3.13 * Building wheel... Successfully built forecast_solar-0.0.0-py3-none-any.whl I: pybuild plugin_pyproject:144: Unpacking wheel built for python3.13 with "installer" module dh_auto_test -O--buildsystem=pybuild I: pybuild base:311: cd /build/reproducible-path/forecast-solar-4.0.0/.pybuild/cpython3_3.12/build; python3.12 -m pytest --no-cov /usr/lib/python3/dist-packages/pytest_asyncio/plugin.py:207: PytestDeprecationWarning: The configuration option "asyncio_default_fixture_loop_scope" is unset. The event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: "function", "class", "module", "package", "session" warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET)) ============================= test session starts ============================== platform linux -- Python 3.12.9, pytest-8.3.4, pluggy-1.5.0 rootdir: /build/reproducible-path/forecast-solar-4.0.0/.pybuild/cpython3_3.12/build configfile: pyproject.toml plugins: syrupy-4.8.1, pytest_freezer-0.4.9, cov-5.0.0, aresponses-3.0.0, asyncio-0.25.1, typeguard-4.4.1 asyncio: mode=Mode.AUTO, asyncio_default_fixture_loop_scope=None collected 15 items tests/test_exceptions.py FFFF. [ 33%] tests/test_forecast.py FF... [ 66%] tests/test_models.py FFFFF [100%] =================================== FAILURES =================================== _______________________________ test_status_400 ________________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_status_400( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test response status 400.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=400, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) with pytest.raises(ForecastSolarRequestError): > assert await forecast_client._request("test") tests/test_exceptions.py:38: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError _______________________________ test_status_401 ________________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_status_401( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test response status 401 or 403.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=401, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) with pytest.raises(ForecastSolarAuthenticationError): > assert await forecast_client._request("test") tests/test_exceptions.py:61: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError _______________________________ test_status_422 ________________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_status_422( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test response status 422.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=422, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) with pytest.raises(ForecastSolarConfigError): > assert await forecast_client._request("test") tests/test_exceptions.py:84: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError _______________________________ test_status_429 ________________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_status_429( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test response status 429.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=429, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("ratelimit.json"), ), ) with pytest.raises(ForecastSolarRatelimitError): > assert await forecast_client._request("test") tests/test_exceptions.py:107: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ______________________________ test_json_request _______________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_json_request( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test JSON response is handled correctly.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) > response = await forecast_client._request("test") tests/test_forecast.py:42: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ____________________________ test_internal_session _____________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m...ing_evening=None, horizon='0,0,0,10,10,20,20,30,30', session=None, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = async def test_internal_session(aresponses: ResponsesMockServer) -> None: """Test internal session is handled correctly.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) async with ForecastSolar( latitude=52.16, longitude=4.47, declination=20, azimuth=10, kwp=2.160, damping=0, horizon="0,0,0,10,10,20,20,30,30", ) as client: > await client._request("test") tests/test_forecast.py:72: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m...ing_evening=None, horizon='0,0,0,10,10,20,20,30,30', session=None, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ___________________________ test_estimated_forecast ____________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = snapshot = SnapshotAssertion(name='snapshot', num_executions=0) forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) @pytest.mark.freeze_time("2024-04-26T12:00:00+02:00") async def test_estimated_forecast( aresponses: ResponsesMockServer, snapshot: SnapshotAssertion, forecast_client: ForecastSolar, ) -> None: """Test estimated forecast.""" aresponses.add( "api.forecast.solar", "/estimate/52.16/4.47/20/10/2.16", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) > forecast: Estimate = await forecast_client.estimate() tests/test_models.py:35: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:208: in estimate data = await self._request( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError __________________ test_estimated_forecast_with_subscription ___________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = snapshot = SnapshotAssertion(name='snapshot', num_executions=0) forecast_key_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) @pytest.mark.freeze_time("2024-04-27T07:00:00+02:00") async def test_estimated_forecast_with_subscription( aresponses: ResponsesMockServer, snapshot: SnapshotAssertion, forecast_key_client: ForecastSolar, ) -> None: """Test estimated forecast.""" aresponses.add( "api.forecast.solar", "/myapikey/estimate/52.16/4.47/20/10/2.16", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "60", "X-Ratelimit-Period": "3600", }, text=load_fixtures("forecast_personal.json"), ), ) > forecast: Estimate = await forecast_key_client.estimate() tests/test_models.py:81: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:208: in estimate data = await self._request( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError __________ test_estimated_forecast_with_subscription_and_actual_value __________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = snapshot = SnapshotAssertion(name='snapshot', num_executions=0) forecast_key_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) @pytest.mark.freeze_time("2024-04-27T07:00:00+02:00") async def test_estimated_forecast_with_subscription_and_actual_value( aresponses: ResponsesMockServer, snapshot: SnapshotAssertion, forecast_key_client: ForecastSolar, ) -> None: """Test estimated forecast.""" aresponses.add( "api.forecast.solar", "/myapikey/estimate/52.16/4.47/20/10/2.16", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "60", "X-Ratelimit-Period": "3600", }, text=load_fixtures("forecast_personal.json"), ), ) > forecast: Estimate = await forecast_key_client.estimate(actual=2.300) tests/test_models.py:127: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:208: in estimate data = await self._request( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ___________________________ test_api_key_validation ____________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'info' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_key_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) async def test_api_key_validation( aresponses: ResponsesMockServer, forecast_key_client: ForecastSolar, ) -> None: """Test API key validation.""" aresponses.add( "api.forecast.solar", "/myapikey/info", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("validate_key.json"), ), ) > assert await forecast_key_client.validate_api_key() is True tests/test_models.py:160: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:180: in validate_api_key await self._request("info", rate_limit=False) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'info' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ____________________________ test_plane_validation _____________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'check/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_plane_validation( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test plane validation.""" aresponses.add( "api.forecast.solar", "/check/52.16/4.47/20/10/2.16", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("validate_plane.json"), ), ) > assert await forecast_client.validate_plane() is True tests/test_models.py:182: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:163: in validate_plane await self._request( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'check/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError --------------------------- snapshot report summary ---------------------------- 3 snapshots unused. Re-run pytest with --snapshot-update to delete unused snapshots. =========================== short test summary info ============================ FAILED tests/test_exceptions.py::test_status_400 - forecast_solar.exceptions.... FAILED tests/test_exceptions.py::test_status_401 - forecast_solar.exceptions.... FAILED tests/test_exceptions.py::test_status_422 - forecast_solar.exceptions.... FAILED tests/test_exceptions.py::test_status_429 - forecast_solar.exceptions.... FAILED tests/test_forecast.py::test_json_request - forecast_solar.exceptions.... FAILED tests/test_forecast.py::test_internal_session - forecast_solar.excepti... FAILED tests/test_models.py::test_estimated_forecast - forecast_solar.excepti... FAILED tests/test_models.py::test_estimated_forecast_with_subscription - fore... FAILED tests/test_models.py::test_estimated_forecast_with_subscription_and_actual_value FAILED tests/test_models.py::test_api_key_validation - forecast_solar.excepti... FAILED tests/test_models.py::test_plane_validation - forecast_solar.exception... ========================= 11 failed, 4 passed in 0.87s ========================= E: pybuild pybuild:389: test: plugin pyproject failed with: exit code=1: cd /build/reproducible-path/forecast-solar-4.0.0/.pybuild/cpython3_3.12/build; python3.12 -m pytest --no-cov I: pybuild base:311: cd /build/reproducible-path/forecast-solar-4.0.0/.pybuild/cpython3_3.13/build; python3.13 -m pytest --no-cov /usr/lib/python3/dist-packages/pytest_asyncio/plugin.py:207: PytestDeprecationWarning: The configuration option "asyncio_default_fixture_loop_scope" is unset. The event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: "function", "class", "module", "package", "session" warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET)) ============================= test session starts ============================== platform linux -- Python 3.13.2, pytest-8.3.4, pluggy-1.5.0 rootdir: /build/reproducible-path/forecast-solar-4.0.0/.pybuild/cpython3_3.13/build configfile: pyproject.toml plugins: syrupy-4.8.1, pytest_freezer-0.4.9, cov-5.0.0, aresponses-3.0.0, asyncio-0.25.1, typeguard-4.4.1 asyncio: mode=Mode.AUTO, asyncio_default_fixture_loop_scope=None collected 15 items tests/test_exceptions.py FFFF. [ 33%] tests/test_forecast.py FF... [ 66%] tests/test_models.py FFFFF [100%] =================================== FAILURES =================================== _______________________________ test_status_400 ________________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_status_400( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test response status 400.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=400, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) with pytest.raises(ForecastSolarRequestError): > assert await forecast_client._request("test") tests/test_exceptions.py:38: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError _______________________________ test_status_401 ________________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_status_401( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test response status 401 or 403.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=401, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) with pytest.raises(ForecastSolarAuthenticationError): > assert await forecast_client._request("test") tests/test_exceptions.py:61: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError _______________________________ test_status_422 ________________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_status_422( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test response status 422.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=422, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) with pytest.raises(ForecastSolarConfigError): > assert await forecast_client._request("test") tests/test_exceptions.py:84: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError _______________________________ test_status_429 ________________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_status_429( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test response status 429.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=429, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("ratelimit.json"), ), ) with pytest.raises(ForecastSolarRatelimitError): > assert await forecast_client._request("test") tests/test_exceptions.py:107: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ______________________________ test_json_request _______________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_json_request( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test JSON response is handled correctly.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) > response = await forecast_client._request("test") tests/test_forecast.py:42: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ____________________________ test_internal_session _____________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m...ing_evening=None, horizon='0,0,0,10,10,20,20,30,30', session=None, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = async def test_internal_session(aresponses: ResponsesMockServer) -> None: """Test internal session is handled correctly.""" aresponses.add( "api.forecast.solar", "/test", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) async with ForecastSolar( latitude=52.16, longitude=4.47, declination=20, azimuth=10, kwp=2.160, damping=0, horizon="0,0,0,10,10,20,20,30,30", ) as client: > await client._request("test") tests/test_forecast.py:72: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m...ing_evening=None, horizon='0,0,0,10,10,20,20,30,30', session=None, ratelimit=None, inverter=None, _close_session=False) uri = 'test' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ___________________________ test_estimated_forecast ____________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = snapshot = SnapshotAssertion(name='snapshot', num_executions=0) forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) @pytest.mark.freeze_time("2024-04-26T12:00:00+02:00") async def test_estimated_forecast( aresponses: ResponsesMockServer, snapshot: SnapshotAssertion, forecast_client: ForecastSolar, ) -> None: """Test estimated forecast.""" aresponses.add( "api.forecast.solar", "/estimate/52.16/4.47/20/10/2.16", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("forecast.json"), ), ) > forecast: Estimate = await forecast_client.estimate() tests/test_models.py:35: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:208: in estimate data = await self._request( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError __________________ test_estimated_forecast_with_subscription ___________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = snapshot = SnapshotAssertion(name='snapshot', num_executions=0) forecast_key_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) @pytest.mark.freeze_time("2024-04-27T07:00:00+02:00") async def test_estimated_forecast_with_subscription( aresponses: ResponsesMockServer, snapshot: SnapshotAssertion, forecast_key_client: ForecastSolar, ) -> None: """Test estimated forecast.""" aresponses.add( "api.forecast.solar", "/myapikey/estimate/52.16/4.47/20/10/2.16", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "60", "X-Ratelimit-Period": "3600", }, text=load_fixtures("forecast_personal.json"), ), ) > forecast: Estimate = await forecast_key_client.estimate() tests/test_models.py:81: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:208: in estimate data = await self._request( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError __________ test_estimated_forecast_with_subscription_and_actual_value __________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = snapshot = SnapshotAssertion(name='snapshot', num_executions=0) forecast_key_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) @pytest.mark.freeze_time("2024-04-27T07:00:00+02:00") async def test_estimated_forecast_with_subscription_and_actual_value( aresponses: ResponsesMockServer, snapshot: SnapshotAssertion, forecast_key_client: ForecastSolar, ) -> None: """Test estimated forecast.""" aresponses.add( "api.forecast.solar", "/myapikey/estimate/52.16/4.47/20/10/2.16", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "60", "X-Ratelimit-Period": "3600", }, text=load_fixtures("forecast_personal.json"), ), ) > forecast: Estimate = await forecast_key_client.estimate(actual=2.300) tests/test_models.py:127: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:208: in estimate data = await self._request( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'estimate/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ___________________________ test_api_key_validation ____________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'info' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_key_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) async def test_api_key_validation( aresponses: ResponsesMockServer, forecast_key_client: ForecastSolar, ) -> None: """Test API key validation.""" aresponses.add( "api.forecast.solar", "/myapikey/info", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("validate_key.json"), ), ) > assert await forecast_key_client.validate_api_key() is True tests/test_models.py:160: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:180: in validate_api_key await self._request("info", rate_limit=False) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key='myapikey', damping=0, dam...e, session=, ratelimit=None, inverter=1.3, _close_session=False) uri = 'info' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError ____________________________ test_plane_validation _____________________________ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'check/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: > result = await dns.query("api.forecast.solar", "A") E aiodns.error.DNSError: (11, 'Could not contact DNS servers') forecast_solar/forecast_solar.py:89: DNSError The above exception was the direct cause of the following exception: aresponses = forecast_client = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) async def test_plane_validation( aresponses: ResponsesMockServer, forecast_client: ForecastSolar, ) -> None: """Test plane validation.""" aresponses.add( "api.forecast.solar", "/check/52.16/4.47/20/10/2.16", "GET", aresponses.Response( status=200, headers={ "Content-Type": "application/json", "X-Ratelimit-Limit": "10", "X-Ratelimit-Period": "1", }, text=load_fixtures("validate_plane.json"), ), ) > assert await forecast_client.validate_plane() is True tests/test_models.py:182: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ forecast_solar/forecast_solar.py:163: in validate_plane await self._request( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ForecastSolar(azimuth=10, declination=20, kwp=2.16, latitude=52.16, longitude=4.47, api_key=None, damping=0, damping_m..., session=, ratelimit=None, inverter=None, _close_session=False) uri = 'check/52.16/4.47/20/10/2.16' async def _request( self, uri: str, *, rate_limit: bool = True, authenticate: bool = True, params: dict[str, Any] | None = None, ) -> Any: """Handle a request to the Forecast.Solar API. A generic method for sending/handling HTTP requests done against the Forecast.Solar API. Args: ---- uri: Request URI, for example, 'estimate' rate_limit: Parse rate limit from response. Set to False for endpoints that are missing rate limiting headers in response. authenticate: Prefix request with api_key. Set to False for endpoints that do not provide authentication. Returns: ------- A Python dictionary (JSON decoded) with the response from the Forecast.Solar API. Raises: ------ ForecastSolarAuthenticationError: If the API key is invalid. ForecastSolarConnectionError: An error occurred while communicating with the Forecast.Solar API. ForecastSolarError: Received an unexpected response from the Forecast.Solar API. ForecastSolarRequestError: There is something wrong with the variables used in the request. ForecastSolarRatelimitError: The number of requests has exceeded the rate limit of the Forecast.Solar API. """ # Forecast.Solar is currently experiencing IPv6 issues. # However, their DNS does return an non-working IPv6 address. # This ensures we use the IPv4 address. dns = DNSResolver() try: result = await dns.query("api.forecast.solar", "A") except DNSError as err: > raise ForecastSolarConnectionError( "Error while resolving Forecast.Solar API address" ) from err E forecast_solar.exceptions.ForecastSolarConnectionError: Error while resolving Forecast.Solar API address forecast_solar/forecast_solar.py:91: ForecastSolarConnectionError --------------------------- snapshot report summary ---------------------------- 3 snapshots unused. Re-run pytest with --snapshot-update to delete unused snapshots. =========================== short test summary info ============================ FAILED tests/test_exceptions.py::test_status_400 - forecast_solar.exceptions.... FAILED tests/test_exceptions.py::test_status_401 - forecast_solar.exceptions.... FAILED tests/test_exceptions.py::test_status_422 - forecast_solar.exceptions.... FAILED tests/test_exceptions.py::test_status_429 - forecast_solar.exceptions.... FAILED tests/test_forecast.py::test_json_request - forecast_solar.exceptions.... FAILED tests/test_forecast.py::test_internal_session - forecast_solar.excepti... FAILED tests/test_models.py::test_estimated_forecast - forecast_solar.excepti... FAILED tests/test_models.py::test_estimated_forecast_with_subscription - fore... FAILED tests/test_models.py::test_estimated_forecast_with_subscription_and_actual_value FAILED tests/test_models.py::test_api_key_validation - forecast_solar.excepti... FAILED tests/test_models.py::test_plane_validation - forecast_solar.exception... ========================= 11 failed, 4 passed in 0.34s ========================= E: pybuild pybuild:389: test: plugin pyproject failed with: exit code=1: cd /build/reproducible-path/forecast-solar-4.0.0/.pybuild/cpython3_3.13/build; python3.13 -m pytest --no-cov dh_auto_test: error: pybuild --test --test-pytest -i python{version} -p "3.12 3.13" returned exit code 13 make: *** [debian/rules:5: binary] Error 25 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/395853 and its subdirectories forecast-solar failed to build from source. removed '/var/lib/jenkins/userContent/reproducible/debian/rbuild/unstable/arm64/forecast-solar_4.0.0-1.rbuild.log' removed '/var/lib/jenkins/userContent/reproducible/debian/rbuild/unstable/arm64/forecast-solar_4.0.0-1.rbuild.log.gz' removed '/var/lib/jenkins/userContent/reproducible/debian/logs/unstable/arm64/forecast-solar_4.0.0-1.build1.log.gz' Fri Feb 21 23:06:49 UTC 2025 W: No second build log, what happened? Compressing the 1st log... b1/build.log: 92.0% -- replaced with stdout INSERT 0 1 INSERT 0 1 DELETE 1 [2025-02-21 23:06:50] INFO: Starting at 2025-02-21 23:06:50.031945 [2025-02-21 23:06:50] INFO: Generating the pages of 1 package(s) [2025-02-21 23:06:50] ERROR: Either /var/lib/jenkins/userContent/reproducible/debian/logs/unstable/amd64/forecast-solar_4.0.0-1.build2.log.gz or /var/lib/jenkins/userContent/reproducible/debian/logdiffs/unstable/amd64/forecast-solar_4.0.0-1.diff.gz is missing [2025-02-21 23:06:50] CRITICAL: https://tests.reproducible-builds.org/debian/unstable/arm64/forecast-solar didn't produce a buildlog, even though it has been built. [2025-02-21 23:06:50] INFO: Finished at 2025-02-21 23:06:50.194833, took: 0:00:00.162894 Fri Feb 21 23:06:50 UTC 2025 - successfully updated the database and updated https://tests.reproducible-builds.org/debian/rb-pkg/unstable/arm64/forecast-solar.html Starting cleanup. /var/lib/jenkins/userContent/reproducible/debian/rbuild/unstable/arm64/forecast-solar_4.0.0-1.rbuild.log: 91.2% -- replaced with /var/lib/jenkins/userContent/reproducible/debian/rbuild/unstable/arm64/forecast-solar_4.0.0-1.rbuild.log.gz [2025-02-21 23:06:50] INFO: Starting at 2025-02-21 23:06:50.567884 [2025-02-21 23:06:50] INFO: Generating the pages of 1 package(s) [2025-02-21 23:06:50] ERROR: Either /var/lib/jenkins/userContent/reproducible/debian/logs/unstable/amd64/forecast-solar_4.0.0-1.build2.log.gz or /var/lib/jenkins/userContent/reproducible/debian/logdiffs/unstable/amd64/forecast-solar_4.0.0-1.diff.gz is missing [2025-02-21 23:06:50] ERROR: Either /var/lib/jenkins/userContent/reproducible/debian/logs/unstable/arm64/forecast-solar_4.0.0-1.build2.log.gz or /var/lib/jenkins/userContent/reproducible/debian/logdiffs/unstable/arm64/forecast-solar_4.0.0-1.diff.gz is missing [2025-02-21 23:06:50] INFO: Finished at 2025-02-21 23:06:50.742324, took: 0:00:00.174448 All cleanup done. Fri Feb 21 23:06:50 UTC 2025 - total duration: 0h 2m 0s. Fri Feb 21 23:06:50 UTC 2025 - reproducible_build.sh stopped running as /tmp/jenkins-script-k5FKiHEp, removing. Finished with result: success Main processes terminated with: code=exited/status=0 Service runtime: 2min 1.118s CPU time consumed: 4.332s