. "$BST_LIB" # BST-TIMEOUT: 900 # TWO python3 starts, and they are the whole cost: a bare # # `import' (the platform gate) and the probe, which takes # # ONE start for sixty-odd measurements. Every other case # # below reads the filesystem or dpkg with shell builtins. # # # # The arithmetic inside the probe is microseconds -- the # # largest object is a 127-bit modulus and a 100-bit MPFR -- # # beside an interpreter start on this arm, which has # # measured at tens of seconds before the first line of the # # program runs. 900 is 450s per start, the same shape # # 99-python3-crc32c gives its three at 1200. # # # # NOT MEASURED ON TARGET YET: this port's first on-target # # run is the batch verify, and the measured elapsed belongs # # in the test plan rather than in this comment, which would # # cost an image build to keep the shipped suite # # byte-identical. # # python3-gmpy2 2.1.5 (meta-openembedded/meta-python # recipes-devtools/python/python3-gmpy2_2.1.5.bb, `inherit pypi setuptools3 # python3native', DEPENDS "gmp mpfr libmpc") on the wasm32-hwjs glibc image. # # ============================================================================ # WHAT THE PACKAGE IS, AND WHY THE BOUNDARY IS THE INTERESTING PART. # ============================================================================ # # gmpy2 is the CPython binding for three libraries this image already carries: # GMP (BRINTOS-210), MPFR (BRINTOS-526) and MPC (BRINTOS-643). It adds no # mathematics of its own -- every value below is computed inside those three and # marshalled back through the extension. # # So this port is a boundary, not an algorithm, and the assertions are chosen for # what a fresh arm breaks at a boundary: GMP's limb width and MPFR's rounding # modes. NEITHER FAILS AS A CRASH. A wrong limb width gives a wrong digit in the # middle of a correct-looking number; a rounding mode that does not reach MPFR # gives a result that is plausible and off by an ulp. That is why every numeric # case here is against a LITERAL and not against another call on the same object. # # ============================================================================ # THE PLATFORM GATE: THE EXTENSION IS THE MODULE. # ============================================================================ # # gmpy2 ships `gmpy2/__init__.py', but it is one line of substance -- # `from .gmpy2 import *' over `gmpy2/gmpy2.cpython-312.so'. There is no # pure-Python half to fall back to, so this is python3-bitarray's shape # (BRINTOS-2032) and not python3-crcmod's: the port either loads a side module or # it delivers nothing. # # THE HISTORY THAT MATTERS FOR READING A RED HERE, because two walls have stood # in this exact place and both are down at this batch's pin: # # * BRINTOS-2046 -- a dlopen'd extension could not BIND the interpreter's C # API, because the target python3 exported no Py* symbol at all. Closed # 2026-09-02: the interpreter now publishes 1715 CPython C API symbols # through WASMLD_DYN_EXPORT_LIBC. # # * BRINTOS-2323 / linux-in-the-browser#184 -- extensions then BOUND and # trapped at first import with `hwjs-user-rt VIOLATION: set-over-set', rc=132, # because a dynamic main published no suspend partition and the -shared link # fell back to attribute keying. Closed: wasmld publishes the partition and # wasmcc-glibc consumes it. # # * BRINTOS-2365 GENERALISED that wiring from one .bbappend to # classes/brintos-python3-dynmain-suspenders.bbclass, which arms EVERY # class-target glibc recipe inheriting setuptools3-base with a `python3' # DEPENDS. gmpy2 inherits setuptools3, and setuptools3-base appends the # python3 edge, so this recipe is armed with no bbappend of its own -- the # class was written so that a port added afterwards needs no edit. # # A THIRD WALL WAS THE ONE THAT ACTUALLY STOPPED THIS PORT, and it was at BUILD # time rather than at load. The stock recipe does not link: the coroutinize pass # refuses GMPy_MPZ_To_Binary, GMPy_XMPZ_To_Binary, GMPy_MPQ_To_Binary, # GMPy_MPFR_To_Binary and mpz_ascii, each # `vla/dynamic-alloca-address-escape-opaque-sink' -- the hwjs-cc#15 class. The # five are exactly the nine TEMP_ALLOC sites in the source, and the layer carries # a bbappend and a one-hunk patch that select the malloc arm gmpy2 already ships # (-DGMPY_NO_ALLOCA). It is named here because a reader meeting a red below will # otherwise go looking at the two load-time walls, which were never the problem. # # So `import' below is expected to pass. It is still written as a gate rather # than as a formality, because it is the one case whose failure explains every # other one, and because that class's own comment records the case where arming a # recipe was NOT sufficient: python3-lz4 and python3-xxhash import cleanly and # then trap in the CALL path on linux-in-the-browser#186, a main-side vectorcall # of a PyMethodDef entry refused by the F152 dispatch consult. gmpy2's whole # surface is module-level functions reached exactly that way. # # THAT IS A PREDICTION AND IT IS LABELLED AS ONE. Nothing in this suite has run # on target: a recipe-scope build cannot boot an image, so every expectation here # is derived from the source and from an x86-64 control, and the batch verify is # the first measurement. The test plan carries the prediction in those words. # # IF THE IMPORT FAILS, every behavioural case is SKIPPED with the reason attached # rather than reded -- sixty reds all naming one platform fact would bury it. # # ============================================================================ # WHERE THE EXPECTED VALUES COME FROM. # ============================================================================ # # THE INTEGER AND RATIONAL VALUES ARE CROSS-CHECKED AGAINST A SECOND, # INDEPENDENT IMPLEMENTATION -- Python's own arbitrary-precision `int', which # does not use GMP. 100!, 3**500, gcd(100!, 3**500) and the modular inverse were # each computed both by the gmpy2 control and by `math.factorial', `**', # `math.gcd' and `pow(a, -1, m)', and the two agree. So a wrong limb width has to # defeat two implementations to pass, and `invert-check' below multiplies the # inverse back out on target as a third check that needs no literal at all. # # THE MPFR AND MPC VALUES ARE FROM AN x86-64 CONTROL, gmpy2 2.2.1 -- NOT 2.1.5. # Stated plainly because it is the one soft spot: no 2.1.5 wheel exists for # CPython 3.12 and this host has no GMP/MPFR/MPC headers to build one from the # sdist. What makes the control valid anyway is that MPFR is CORRECTLY ROUNDED by # its own specification: at a stated precision and rounding mode the result is a # property of the mathematics, not of the binding's version. The four constants # are additionally checked against their PUBLISHED digits (`*-pub25' below), which # is a check the control cannot influence at all. # # WHERE 2.1.5 AND THE CONTROL COULD DIVERGE, THE 2.1.5 SOURCE WAS READ INSTEAD. # `invert()' with no inverse is the case that motivated it -- the answer is # ZeroDivisionError, read off 2.1.5's own src/gmpy2_mpz_misc.c, which calls # ZERO_ERROR("invert() no inverse exists") rather than returning 0. Every gmpy2 # name this suite drives was confirmed present in the 2.1.5 source before it was # used. # # 25 DIGITS, NOT 30, for the published-constant cases. 100 bits is ~30.1 decimal # digits, so the correctly-rounded value starts diverging from the true constant # around digit 30 -- ln(2) does so at digit 30 exactly. 25 is comfortably inside # for all four, which is what makes those cases a statement about the constant # rather than about the precision. P=python3-gmpy2 # ============================================================================ # The suite proper. # ============================================================================ # # Emitted FIRST and unconditionally: a suite that dies before its first result # line is indistinguishable from one that was never scheduled. bst_ok $P/suite-canary require python3 $P/python3-present SITE=/usr/lib/python3.12/site-packages PKGDIR=$SITE/gmpy2 SO=$PKGDIR/gmpy2.cpython-312.so DI=$SITE/gmpy2-2.1.5.dist-info PKGLIST=/var/lib/dpkg/info/python3-gmpy2.list PROBE=$BST_DATA/python3-gmpy2-probe.py tmp=${TMPDIR:-/tmp} W=$tmp/bst-gmpy2.$$ mkdir -p "$W" || { bst_notok $P/workdir "cannot create $W"; exit 0; } # ---------------------------------------------------------------- packaging -- # These run before any import, so a red here says the .deb is wrong rather than # that the module misbehaves -- which matters for this port, because a missing # .so and an unloadable one both end at the same ImportError. check $P/site-packages-dir test -d "$SITE" check $P/package-dir test -d "$PKGDIR" check $P/init-present test -f "$PKGDIR/__init__.py" check $P/extension-present test -f "$SO" check $P/extension-nonempty test -s "$SO" # THE SONAME CARRIES NO PLATFORM TRIPLE. Upstream names the extension # `gmpy2.cpython-312-.so'; poky's python3 reports an empty EXT_SUFFIX # triple on this arm, so the file is plain `gmpy2.cpython-312.so'. The import # resolves through importlib.machinery.EXTENSION_SUFFIXES, so the two must agree # -- and if a future poky restored the triple this case reds before the import # does and says why. check_eq $P/extension-name "gmpy2.cpython-312.so" \ "$(cd "$PKGDIR" 2>/dev/null && echo gmpy2.cpython-312*.so)" # EXACTLY ONE SHARED OBJECT. gmpy2 builds one extension; a second would mean the # build produced something this port did not expect and did not measure. check_eq $P/pkg-one-so "1" \ "$(ls "$PKGDIR"/*.so 2>/dev/null | wc -l | tr -d ' ')" # THE PUBLIC HEADERS SHIP IN THE RUNTIME PACKAGE. gmpy2 exposes a C API and a # Cython interface for other extensions to build against, and the packaging puts # all three beside the .so rather than in -dev. Measured off the built .deb. # They are asserted because a split that moved them to -dev would silently take # this package's whole third-party API off the image. check $P/header-c test -s "$PKGDIR/gmpy2.h" check $P/header-pxd test -s "$PKGDIR/gmpy2.pxd" check $P/header-init-pxd test -s "$PKGDIR/__init__.pxd" # NO EXECUTABLE. setup.py declares no console_scripts, so nothing lands in # /usr/bin. Asserted so a release that started shipping one is noticed. check $P/no-bin-script test ! -e /usr/bin/gmpy2 # --------------------------------------------------------------- dist-info --- check $P/dist-info-dir test -d "$DI" check $P/dist-info-metadata test -f "$DI/METADATA" check_out $P/dist-info-version "Version: 2.1.5" cat "$DI/METADATA" check_out $P/dist-info-name "Name: gmpy2" cat "$DI/METADATA" check $P/dist-info-wheel test -f "$DI/WHEEL" # NOT PURELIB, stated by the packaging itself: `Root-Is-Purelib: false' is the # wheel's own claim that it contains something architecture-specific, which is # the one-line summary of why this port is not a pure-Python one. check_out $P/dist-info-not-purelib "Root-Is-Purelib: false" cat "$DI/WHEEL" # ------------------------------------------------------------ the .deb file --- # The installed file list, read once into $W. The copy is what the loop below # redirects from: a stdin redirect the top-level shell performs on the virtiofs # root makes dup2(2) return 513 and ENDS the suite silently (brintos/linux#73), # and $W is on tmpfs. cat "$PKGLIST" > "$W/pkgf" 2>/dev/null || : > "$W/pkgf" check $P/pkg-list-present test -s "$W/pkgf" # Every payload path is under site-packages and is the extension, a .py, or a # dist-info file. _bad="" while IFS= read -r _f; do case "$_f" in ''|/usr/lib/python3.12/site-packages) continue ;; */) continue ;; esac [ -d "$_f" ] && continue case "$_f" in *.so|*.py|*.pyc) ;; # gmpy2 ships its C and Cython headers INSIDE the runtime package, not in # -dev: gmpy2.h, gmpy2.pxd and __init__.pxd are the public API another # extension compiles against. Measured off the built .deb, not assumed. *.h|*.pxd) ;; */METADATA|*/WHEEL|*/RECORD|*/INSTALLER|*/LICENSE|*/LICENSE.*) ;; */COPYING|*/COPYING.*|*/entry_points.txt|*/top_level.txt) ;; *) _bad="$_bad $_f" ;; esac done < "$W/pkgf" check_eq $P/pkg-only-expected "" "$_bad" # ------------------------------------------------------------- dpkg status --- sed -n '/^Package: python3-gmpy2$/,/^$/p' /var/lib/dpkg/status > "$W/st" 2>/dev/null check $P/pkg-known test -s "$W/st" check_out $P/pkg-installed "Status: install ok installed" cat "$W/st" check_out $P/pkg-arch "Architecture: wasm32" cat "$W/st" # THE DEPENDENCY SET, AND IT IS python3-core AND NOTHING ELSE -- measured off # the built .deb's control stanza, not predicted. # # That is worth an assertion precisely because it looks wrong. The recipe DEPENDS # on gmp, mpfr and libmpc, so a reader expects three library packages here. They # are absent because all three are STATICALLY ABSORBED into the extension: the # module's `env' imports are 79 CPython API symbols, the hwjs runtime ABI and # libc, and carry no __gmp_*, mpfr_* or mpc_* entry at all (the __gmp_* names in # the module are GOT.func/GOT.mem relocations against its own definitions, which # is what an 8.8 MB .so is made of). # # So a NEW name appearing in Depends means the three stopped being absorbed -- # BRINTOS_PACKAGE_SHARED reaching one of them -- and the extension now needs a # needed-entry that must actually be on the image. That is a change worth # failing on rather than discovering as an ImportError. grep '^Depends:' "$W/st" > "$W/dep" 2>/dev/null || : > "$W/dep" check $P/pkg-depends-line test -s "$W/dep" check_out $P/pkg-depends-python3-core "python3-core" cat "$W/dep" check_eq $P/pkg-depends-exactly-core "Depends: python3-core" \ "$(cat "$W/dep" 2>/dev/null)" # ------------------------------------------------------------------ import --- # THE PLATFORM GATE, and the first of two python3 starts. See the header: this is # a dlopen of a side module whose CPython API imports bind against the dynamic # main, over a link seeded from that main's published suspend partition. check $P/import python3 -c 'import gmpy2' # ------------------------------------------------------------------- probe --- if [ ! -f "$PROBE" ]; then bst_notok $P/probe-present "probe missing at $PROBE" exit 0 fi bst_ok $P/probe-present # THE SECOND START, and the one that carries the suite. Bounded at 600s inside # the 900s suite budget so a hung probe is reported as a probe overrun with the # last line it printed, rather than as a suite timeout that says nothing about # where it stopped. # # The probe EXITS 0 even when the import fails, so this case passing means "the # probe ran", not "the module works" -- `import-ok' below carries that. bst_probe $P/probe-run "$W/probe" 600 python3 "$PROBE" OUT="$W/probe" # pv : the value the probe emitted for , or empty. `head -n 1' because # a key is emitted once and a duplicate would otherwise concatenate; `cut -f2-' # because a value may legitimately contain `='. pv() { grep "^$1=" "$OUT" 2>/dev/null | head -n 1 | cut -d= -f2-; } # The probe reached its end. Every case below reads a value it printed, so a # probe that stopped early would otherwise become a run of empty-string # mismatches that all name the wrong thing. check_eq $P/probe-complete "yes" "$(pv probe_done)" # ------------------------------------------------------- the gate's verdict --- _imp=$(pv import_ok) check_eq $P/import-ok "yes" "$_imp" if [ "$_imp" != "yes" ]; then # The extension did not load. Skip -- not red -- every behavioural case, each # with the reason attached: they are all one fact, and `import' and `import-ok' # above are the two reds that carry the verdict. _why="gmpy2.cpython-312.so did not load: $(pv import_error)" for _c in probe-count module-answered ext-file ext-basename ext-is-so \ version mp-version mpfr-version mpc-version \ mpz-type mpq-type mpfr-type mpc-type \ fac100 pow3-500 gcd invert invert-check \ is-prime-m127 is-prime-m127m1 \ mpq-sum mpq-prod mpq-harm20 mpq-num mpq-den \ prec-set pi-d40 e-d40 sqrt2-d40 log2-d40 \ pi-pub25 e-pub25 sqrt2-pub25 log2-pub25 \ sqrt2-up sqrt2-down sqrt2-near up-gt-down near-eq-down \ near-eq-up up-down-gap \ mpc-abs mpc-sq-eq mpc-sq-re mpc-sq-im \ mpc-sqrtm1-eq mpc-sqrtm1-re mpc-sqrtm1-im \ mpc-expi-re mpc-expi-im \ sqrt-neg-nan div0-inf \ err-div0-mpz err-floordiv0 err-mod0 err-div0-mpq err-mpq-den0 \ err-invert-ni err-mpz-badstr err-mpfr-badstr mpz-base16; do bst_skip $P/$_c "$_why" done exit 0 fi check_eq $P/probe-count "62" "$(pv emitted_count)" # ---------------------------------------------------------------- identity --- # WHICH COPY ANSWERED, and that the extension beneath the package is what loaded. # Without these the values below would be statements about whatever was first on # sys.path. check_eq $P/module-answered "$PKGDIR/__init__.py" "$(pv module_file)" check_eq $P/ext-file "$SO" "$(pv ext_file)" check_eq $P/ext-basename "gmpy2.cpython-312.so" "$(pv ext_basename)" check_eq $P/ext-is-so "yes" "$(pv ext_is_so)" # The version the recipe pins. check_eq $P/version "2.1.5" "$(pv version)" # THE THREE LIBRARY VERSIONS THIS IMAGE SHIPS. gmpy2 reports what it was COMPILED # against, so these assert that the recipe's DEPENDS resolved to this image's own # gmp, mpfr and libmpc rather than to something staged from elsewhere -- and a # version bump in any of the three lands here as a visible, attributable red # rather than as a silently different numeric result. check_eq $P/mp-version "GMP 6.3.0" "$(pv mp_version)" check_eq $P/mpfr-version "MPFR 4.2.1" "$(pv mpfr_version)" check_eq $P/mpc-version "MPC 1.3.1" "$(pv mpc_version)" check_eq $P/mpz-type "mpz" "$(pv mpz_type)" check_eq $P/mpq-type "mpq" "$(pv mpq_type)" check_eq $P/mpfr-type "mpfr" "$(pv mpfr_type)" check_eq $P/mpc-type "mpc" "$(pv mpc_type)" # --------------------------------------------- mpz: the limb-width assertion -- # Every value here is well past 64 bits, and each was computed twice on the # control -- once by gmpy2 and once by Python's own int, which does not use GMP. check_eq $P/fac100 \ "93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000" \ "$(pv fac100)" check_eq $P/pow3-500 \ "36360291795869936842385267079543319118023385026001623040346035832580600191583895484198508262979388783308179702534403855752855931517013066142992430916562025780021771247847643450125342836565813209972590371590152578728008385990139795377610001" \ "$(pv pow3_500)" check_eq $P/gcd "79766443076872509863361" "$(pv gcd_val)" # The modular inverse of a 95-bit value modulo the Mersenne prime 2**127-1. check_eq $P/invert "144365890609919947541009158643259450776" "$(pv invert_val)" # AND THE INVERSE MULTIPLIED BACK OUT, which needs no literal: a*inv mod m is 1 # or the inverse is wrong. This is the case that cannot be satisfied by a # coincidence of digits. check_eq $P/invert-check "1" "$(pv invert_check)" # Primality over the same modulus: 2**127-1 is a Mersenne prime, 2**127-2 is even. check_eq $P/is-prime-m127 "True" "$(pv is_prime_m127)" check_eq $P/is-prime-m127m1 "False" "$(pv is_prime_m127m1)" # ------------------------------------------------- mpq: exact, or not at all -- # A rational that is not exact is not a rational. Each has a closed form a float # would miss, and the harmonic sum's denominator is lcm(1..20) reduced. check_eq $P/mpq-sum "1/2" "$(pv mpq_sum)" check_eq $P/mpq-prod "1" "$(pv mpq_prod)" check_eq $P/mpq-harm20 "55835135/15519504" "$(pv mpq_harm20)" # mpq(6,4) must arrive already in lowest terms. check_eq $P/mpq-num "3" "$(pv mpq_num)" check_eq $P/mpq-den "2" "$(pv mpq_den)" # --------------------------------------------- mpfr at a stated precision ---- # The precision is asserted first, because every digit below is a statement about # the value AT THIS PRECISION. check_eq $P/prec-set "100" "$(pv prec_set)" # The exact 40-digit mantissa MPFR rounds each constant to at precision 100, # with its decimal exponent after `@'. Correctly rounded by MPFR's specification, # so these are properties of the mathematics and the precision. check_eq $P/pi-d40 "3141592653589793238462643383279333315644@1" "$(pv pi_d40)" check_eq $P/e-d40 "2718281828459045235360287471351234815084@1" "$(pv e_d40)" check_eq $P/sqrt2-d40 "1414213562373095048801688724209176249925@1" "$(pv sqrt2_d40)" check_eq $P/log2-d40 "6931471805599453094172321214579818635663@0" "$(pv log2_d40)" # The same four constants against their PUBLISHED digits, rounded to 25 -- the # check the control cannot influence, and the one that says these are pi, e, # sqrt(2) and ln(2) rather than merely reproducible. check_eq $P/pi-pub25 "3141592653589793238462643" "$(pv pi_pub25)" check_eq $P/e-pub25 "2718281828459045235360287" "$(pv e_pub25)" check_eq $P/sqrt2-pub25 "1414213562373095048801689" "$(pv sqrt2_pub25)" check_eq $P/log2-pub25 "6931471805599453094172321" "$(pv log2_pub25)" # ------------------------------------------------------- the rounding modes --- # sqrt(2) is irrational, so at precision 100 it lands strictly between two # representable numbers and the modes have somewhere to differ. THE DIRECTION is # what is asserted and not only the digits: a rounding mode that never reached # MPFR would give three identical results, which `up-gt-down' catches and the # three digit cases alone would not. check_eq $P/sqrt2-up "1414213562373095048801688724210753971736@1" "$(pv sqrt2_up)" check_eq $P/sqrt2-down "1414213562373095048801688724209176249925@1" "$(pv sqrt2_down)" check_eq $P/sqrt2-near "1414213562373095048801688724209176249925@1" "$(pv sqrt2_near)" # RoundUp must exceed RoundDown -- the documented direction, and the case that # fails if the mode is ignored. check_eq $P/up-gt-down "yes" "$(pv up_gt_down)" # RoundToNearest must land on whichever of the two the true value is closer to. # For sqrt(2) at precision 100 that is RoundDown, measured on the control; both # halves are asserted so that "nearest equals down" is a reading rather than a # coincidence of a mode that did nothing. check_eq $P/near-eq-down "yes" "$(pv near_eq_down)" check_eq $P/near-eq-up "no" "$(pv near_eq_up)" # The gap between them is one ulp at precision 100 -- non-zero, which is the # other way of saying the modes moved. check_eq $P/up-down-gap "1577721810442023610823457130565572459346@-29" "$(pv up_down_gap)" # --------------------------------------------------------------------- mpc --- # |3+4i| = 5 exactly, so this one is not a rounding statement at all. check_eq $P/mpc-abs "5000000000000000000000000000000000000000@1" "$(pv mpc_abs)" # (1+i)^2 = 2i and sqrt(-1) = i, both exact. Asserted by value and by component # rather than through str(), whose formatting is not a documented contract. check_eq $P/mpc-sq-eq "yes" "$(pv mpc_sq_eq)" check_eq $P/mpc-sq-re "0" "$(pv mpc_sq_re)" check_eq $P/mpc-sq-im "2" "$(pv mpc_sq_im)" check_eq $P/mpc-sqrtm1-eq "yes" "$(pv mpc_sqrtm1_eq)" check_eq $P/mpc-sqrtm1-re "0" "$(pv mpc_sqrtm1_re)" check_eq $P/mpc-sqrtm1-im "1" "$(pv mpc_sqrtm1_im)" # exp(i) = cos(1) + i sin(1), each against its published digits at 25. check_eq $P/mpc-expi-re "5403023058681397174009366" "$(pv mpc_expi_re)" check_eq $P/mpc-expi-im "8414709848078965066525023" "$(pv mpc_expi_im)" # ------------------------------------------------------------- the specials -- # A domain error must produce MPFR's nan and inf rather than an abort inside the # C library, which is the failure mode that matters on a fresh arm. check_eq $P/sqrt-neg-nan "yes" "$(pv sqrt_neg_nan)" check_eq $P/div0-inf "yes" "$(pv div0_inf)" # -------------------------------------------------------------- error paths --- # Each must raise the DOCUMENTED exception. The alternative here is not a wrong # answer but an abort inside GMP, which takes the interpreter with it -- so these # are as much a statement about the port as the arithmetic is. check_eq $P/err-div0-mpz "ZeroDivisionError" "$(pv err_div0_mpz)" check_eq $P/err-floordiv0 "ZeroDivisionError" "$(pv err_floordiv0)" check_eq $P/err-mod0 "ZeroDivisionError" "$(pv err_mod0)" check_eq $P/err-div0-mpq "ZeroDivisionError" "$(pv err_div0_mpq)" check_eq $P/err-mpq-den0 "ZeroDivisionError" "$(pv err_mpq_den0)" # invert(4, 8): gcd is 4, so no inverse exists. ZeroDivisionError, read off # 2.1.5's own src/gmpy2_mpz_misc.c rather than taken from the 2.2.1 control. check_eq $P/err-invert-ni "ZeroDivisionError" "$(pv err_invert_ni)" check_eq $P/err-mpz-badstr "ValueError" "$(pv err_mpz_badstr)" check_eq $P/err-mpfr-badstr "ValueError" "$(pv err_mpfr_badstr)" # A base that IS accepted, so the two malformed-string cases above are not # passing on a parser that refuses everything. check_eq $P/mpz-base16 "255" "$(pv mpz_base16)"