# BST-TIMEOUT: 1800 # ONE python3 start, and that is the whole cost. The # # interpreter is a ~63 MB static image and a start alone # # has been minutes on a loaded guest, which is why # # 99-python3 carries the same bound; the runner's 60s # # default would report the port as broken when what ran # # out was the clock. NOT MEASURED ON TARGET YET -- this # # port's first on-target run is the batch image's verify. # # python3-distlib 0.3.8 (meta-python recipes-devtools/python) -- the low-level # packaging library pip's vendored code is built on: metadata parsing, PEP 440 # version comparison, wheel handling and script generation, all offline. # # ============================================================================ # ONE INTERPRETER START, AND THAT IS A CONSTRAINT RATHER THAN A STYLE. # ============================================================================ # # The host-side verify harness ends a boot at 2048 MiB/min of guest-process # growth averaged over 120s while this runtime grows ~9000 MiB/min whatever the # guest does (BRINTOS-1682), and brintos-selftest replays a suite's output only # when the suite ENDS (BRINTOS-1742) -- so a suite making ~25 interpreter starts # is removed mid-run and loses every case it had already passed. That is what a # one-file 99-gyp did on the batch-1649 image: ==BST-BEGIN== and not one case # line, on six boots including a solo one. # # So every Python-side assertion in this port lives in ONE script, # data/python3-distlib-probe.py, run ONCE. The shell below asserts the file # layout and the packaging -- neither of which needs an interpreter -- and then # compares the probe's transcript against a control, group by group. # # ============================================================================ # HOW THE LANGUAGE-LEVEL CASES ARE PINNED: THEY ARE NOT PINNED HERE. # ============================================================================ # # The probe's output is compared line for line against the SAME SCRIPT's output # under this build's own native CPython 3.12.13 (the x86-64 python3-native from # tmp-glibc/sysroots-components), against the SAME distlib 0.3.8 tree unpacked # from the very .deb this recipe produced. Recorded 2026-08-31. distlib is pure # Python and nothing the probe touches depends on pointer width, byte order, # the platform string, the clock or hash randomisation -- which is what makes # "identical to x86-64" a real claim rather than a transcription of whatever # this arm happened to do. # # THE CONTROL CARRIES ONE UPSTREAM BUG, DELIBERATELY. `ver.pre.1!2.0` reads # RAISED:TypeError -- NormalizedVersion.is_prerelease indexes an epoch version's # parsed tuple as though the epoch were absent, in distlib 0.3.8, on BOTH ARMS. # It is an upstream distlib defect, not a brintOS one, it reproduces off-target # on x86-64, and nothing is filed for it # (docs/conventions/no-public-upstream-reports.md). It is kept rather than # edited out because the arms AGREEING about it is itself the assertion: a # version scheme that started answering differently here would be the port # diverging, whichever direction it moved. # # ============================================================================ # WHAT THE PROBE ACTUALLY EXERCISES, AND WHY EACH LAYER IS SEPARATE. # ============================================================================ # # ver.* PEP 440 parsing, prerelease classification, the versions the # scheme must REFUSE, and ordering through the scheme's own sort key # -- the entry point pip's resolver calls, not __lt__ on the objects. # match.* NormalizedMatcher: eight operators against one candidate pool, so # the table says each operator was implemented rather than that one # happened to answer. An unparseable candidate must RAISE, not # return False. # meta.* distlib.metadata.Metadata over data/python3-distlib-METADATA: # name, version, summary, the requirement list, and the environment # markers evaluated through markers.interpret. # wheel.* distlib.wheel.Wheel over a fixture wheel staged in a scratch tree: # the PEP 427 filename fields, the WHEEL dict, the contained # METADATA, the member listing, and verify() -- which re-hashes every # member against RECORD. A deliberately tampered copy must FAIL it, # because a verify() turned into a no-op looks identical otherwise. # net.* distlib.locators and distlib.index are the two modules that open # sockets and NEITHER IS IMPORTED. Asserted against sys.modules, so # an edit that pulls one in by accident is a red rather than a suite # that quietly starts dialling PyPI from inside a browser tab. # # THE FIXTURES ARE STAGED ON TMPFS BEFORE THE PROBE RUNS. Both are read by the # probe, and a read of the virtiofs root is not the hazard -- a top-level stdin # REDIRECT is (brintos/linux#73) -- but the wheel cannot be read from data/ for # a second reason that is not about the platform at all: distlib parses the # PEP 427 fields out of the FILENAME, and brintos-selftests installs data/ flat, # so the archive ships as python3-distlib-bstdemo-1.2.3-py3-none-any.whl and # must be re-staged under its real name to be openable. # # THE WHEEL IS GENERATED, NOT VENDORED, and deterministically: every member # carries a fixed timestamp and mode, so a re-run of # src/python3_distlib_mkwheel.py reproduces the archive byte for byte. RECORD # carries the real hashes, which is what makes verify() a real assertion. # # ============================================================================ # THE .so CENSUS IS THE PORT'S LOAD-BEARING NEGATIVE. # ============================================================================ # # This image's python3 is ONE STATIC BINARY with an empty lib-dynload and no # dynamic loader (BRINTOS-1476), so a module carrying a compiled extension could # not be imported on target at all. distlib is portable here precisely because # it carries none. pkg-no-shared-object asserts that directly against the # package's own file list, so a version bump that grew a compiled speedup fails # loudly here rather than at `import` on a user's target. # # SIX .exe FILES SHIP, AND THAT IS THE RECIPE RATHER THAN A DEFECT. distlib # vendors t32/t64/t64-arm/w32/w64/w64-arm.exe -- prebuilt WINDOWS launcher stubs # distlib.scripts embeds when it generates a console script FOR Windows. They # are inert data on this image, they are 0644 and not executable, and they are # the only non-Python files in the package. pkg-exe-stubs asserts them present # and non-executable, because "the launchers were dropped" and "the launchers # became executable payloads" are both changes this port would want to hear # about. . "$BST_LIB" S=99-python3-distlib # THE CANARY, which asserts nothing about distlib. # # A suite's FIRST BST result line is lost between the guest's printf and the # host's read -- per-suite, deterministic, and NOT a defect in the host tool # (BRINTOS-1149 established that and filed it as linux-in-the-browser#177). # The harness reports the deficit but computes its pass count as total-minus- # failures, so THE DROPPED LINE IS COUNTED AS A PASS. A nothing-case therefore # stands in that position and the eaten line costs nothing; every real case # below is still counted. It sits ahead of the `require` guards deliberately, # because a `require` that skips would emit its skip as the first line and be # eaten too. # # Remove this case when linux-in-the-browser#177 is fixed. bst_ok $S/suite-canary require python3 $S/python3-present VER=0.3.8 SP=/usr/lib/python3.12/site-packages PKG=$SP/distlib DI=$SP/distlib-$VER.dist-info PKGLIST=/var/lib/dpkg/info/python3-distlib.list D=$BST_DATA W=${TMPDIR:-/tmp}/bst-distlib.$$ if mkdir -p "$W"; then bst_ok $S/scratch else bst_notok $S/scratch "cannot create $W"; exit 0; fi # ------------------------------------------------------------- what shipped -- check $S/pkg-tree test -d "$PKG" check $S/pkg-init test -f "$PKG/__init__.py" check $S/pkg-version test -f "$PKG/version.py" check $S/pkg-metadata test -f "$PKG/metadata.py" check $S/pkg-wheel test -f "$PKG/wheel.py" check $S/pkg-database test -f "$PKG/database.py" check $S/pkg-scripts test -f "$PKG/scripts.py" check $S/pkg-util test -f "$PKG/util.py" check $S/pkg-compat test -f "$PKG/compat.py" check $S/pkg-markers test -f "$PKG/markers.py" check $S/pkg-locators test -f "$PKG/locators.py" check $S/pkg-index test -f "$PKG/index.py" check $S/pkg-manifest test -f "$PKG/manifest.py" check $S/pkg-resources test -f "$PKG/resources.py" # The .pyc are built at do_install, not on first import: the tree is 0644 under # a root the guest cannot write, so a missing cache would mean every invocation # recompiles the module it is about to use. check $S/pkg-pyc-init test -f "$PKG/__pycache__/__init__.cpython-312.pyc" check $S/pkg-pyc-version test -f "$PKG/__pycache__/version.cpython-312.pyc" check $S/pkg-pyc-wheel test -f "$PKG/__pycache__/wheel.cpython-312.pyc" # The installed distribution metadata: the version pin, read from the tree the # recipe installed rather than from the module's own __version__ (which the # probe asserts separately, so the two must agree). check $S/dist-info test -d "$DI" check_out $S/dist-name "Name: distlib" cat "$DI/METADATA" check_out $S/dist-version "Version: $VER" cat "$DI/METADATA" check $S/dist-record test -s "$DI/RECORD" check_out $S/dist-toplevel "distlib" cat "$DI/top_level.txt" # ------------------------------------------ the negative the port rests on --- # Read from the PACKAGE'S OWN FILE LIST, not from a find over site-packages: a # find would also census whatever a co-tenant python package installed there, # and the assertion is about THIS package's payload. Copied to tmpfs first -- # never redirected from the rootfs (brintos/linux#73). cat "$PKGLIST" > "$W.pkgf" 2>/dev/null || : > "$W.pkgf" if [ -s "$W.pkgf" ]; then bst_ok $S/pkgfiles-loaded else bst_notok $S/pkgfiles-loaded "$PKGLIST is empty or absent"; fi _so=$(grep -c '\.so$' "$W.pkgf" 2>/dev/null) case "$_so" in ''|*[!0-9]*) _so=0 ;; esac if [ "$_so" -eq 0 ]; then bst_ok $S/pkg-no-shared-object else bst_notok $S/pkg-no-shared-object "python3-distlib ships $_so shared object(s) -- this image's python3 has an empty lib-dynload and no dynamic loader (BRINTOS-1476), so they cannot be imported"; fi # THE SIX WINDOWS LAUNCHER STUBS. Present, and NOT executable: they are data # distlib.scripts embeds into a generated Windows console script, and this image # can neither run nor need them. # # THE MODE IS READ OFF `ls -l', NOT `test -x', as 88-p910nd and 99-isomd5sum do. # These stubs are mode 0644, and that is the exact case `test -x' cannot answer # on this arm: as root the shell takes the root shortcut only for a file with # some execute bit set, so a 0644 file falls through to the supplementary-group # check and getgroups(2) is ENOSYS (brintos/linux#19). The builtin then prints # "sh: getgroups: Function not implemented" and ABORTS THE WHOLE SCRIPT -- it # truncated this suite after 26 cases on the batch-1826 image. _exe=0; _bad="" for _f in t32 t64 t64-arm w32 w64 w64-arm; do if [ -f "$PKG/$_f.exe" ]; then _exe=$((_exe+1)) _m=$(ls -l "$PKG/$_f.exe" 2>/dev/null | cut -c1-10) [ "$_m" = "-rw-r--r--" ] || _bad="$_bad $_f.exe($_m)" fi done if [ "$_exe" -eq 6 ] && [ -z "$_bad" ]; then bst_ok $S/pkg-exe-stubs else bst_notok $S/pkg-exe-stubs "found $_exe of 6 launcher stubs (want 6), not mode 0644:$_bad" fi # ---------------------------------------------------- the packaging on target # THE PACKAGE DATABASE IS THE ONLY PLACE THIS IS VISIBLE. Every case above would # read the same if some other package had installed the same bytes, so they # cannot say that python3-distlib is a real package the image installs rather # than an IMAGE_INSTALL line that resolved to nothing. # # THE ANCHORS ARE EXACT ON PURPOSE. `python3-distlib` is a PREFIX of # `python3-distlib-dev` and `-dbg`, so an unanchored match would report one # stanza as another and make the -dev/-dbg cases unfalsifiable. sed -n '/^Package: python3-distlib$/,/^$/p; /^Package: python3-distlib-dev$/,/^$/p; /^Package: python3-distlib-dbg$/,/^$/p' \ /var/lib/dpkg/status > "$W.st" 2>/dev/null _pkg=""; _main=0; _dev=0; _dbg=0; _mver=""; _dep="" while IFS= read -r l; do case "$l" in "Package: python3-distlib") _pkg=main; _main=1 ;; "Package: python3-distlib-dev") _pkg=dev; _dev=1 ;; "Package: python3-distlib-dbg") _pkg=dbg; _dbg=1 ;; "Version: "*) [ "$_pkg" = main ] && _mver=${l#Version: } ;; "Depends: "*) [ "$_pkg" = main ] && _dep=${l#Depends: } ;; esac done < "$W.st" if [ "$_main" -eq 1 ]; then bst_ok $S/pkg-known else bst_notok $S/pkg-known "dpkg does not know the package python3-distlib"; fi check_eq $S/pkg-version "$VER-r0" "$_mver" # -dev AND -dbg ARE BUILT AND MUST NOT BE INSTALLED. Both are empty here: a pure # Python package has no headers and no debug payload. An IMAGE_INSTALL that # pulled them in would add two packages to the manifest for no payload at all, # and nothing else in this suite would notice. if [ "$_dev" -eq 0 ]; then bst_ok $S/pkg-no-dev else bst_notok $S/pkg-no-dev "python3-distlib-dev is installed -- it is an empty placeholder"; fi if [ "$_dbg" -eq 0 ]; then bst_ok $S/pkg-no-dbg else bst_notok $S/pkg-no-dbg "python3-distlib-dbg is installed -- it is an empty placeholder"; fi # THE RUNTIME DEPENDENCY THE PORT ADDED, READ BACK OFF THE TARGET. The stock # meta-python recipe declares python3-core alone, which cannot import one module # of the tree: distlib/compat.py is on every import path and reaches ssl, # urllib, http.client, xmlrpc.client, queue and html.parser at module scope. A # red here is the bbappend's RDEPENDS having gone missing, and it is why the # probe's every case would otherwise fail at once. for _d in python3-core python3-io python3-netclient python3-xmlrpc \ python3-threading python3-html python3-compression python3-email \ python3-json python3-logging python3-crypt python3-datetime \ python3-shell python3-compile; do case "$_dep" in *"$_d"*) bst_ok "$S/pkg-depends-$_d" ;; *) bst_notok "$S/pkg-depends-$_d" "python3-distlib does not Depend on $_d -- Depends: $(_bst_1line "$_dep")" ;; esac done # ------------------------------------------------------- stage the fixtures -- # Both onto tmpfs, and the wheel under its PEP 427 name: distlib parses the # name/version/tags out of the FILENAME, and brintos-selftests installs data/ # flat, so the shipped basename is not one distlib can open. if cp "$D/python3-distlib-METADATA" "$W/bst-fixture-METADATA" 2>/dev/null; then bst_ok $S/fixture-metadata else bst_notok $S/fixture-metadata "$D/python3-distlib-METADATA did not ship" rm -rf "$W" "$W.pkgf" "$W.st"; exit 0 fi if cp "$D/python3-distlib-bstdemo-1.2.3-py3-none-any.whl" \ "$W/bstdemo-1.2.3-py3-none-any.whl" 2>/dev/null; then bst_ok $S/fixture-wheel else bst_notok $S/fixture-wheel "$D/python3-distlib-bstdemo-1.2.3-py3-none-any.whl did not ship" rm -rf "$W" "$W.pkgf" "$W.st"; exit 0 fi # ------------------------------------------------- the one interpreter start -- # In a SUBSHELL: a redirect the top-level shell performs can end the suite # outright, and the runner would then count every case above as a pass # (brintos/linux#73). stdout and stderr both land in the transcript, because a # warning on stderr is a divergence from the control and must be visible as one. PROBE_OUT=$W/probe.out PROBE_EXP=$D/python3-distlib-probe.expected ( python3 "$D/python3-distlib-probe.py" "$W" "$D" > "$PROBE_OUT" 2>&1 ) probe_rc=$? if [ "$probe_rc" -ne 0 ]; then bst_notok $S/probe-run "rc=$probe_rc $(_bst_1line "$(cat "$PROBE_OUT" 2>/dev/null)")" else bst_ok $S/probe-run fi if [ -f "$PROBE_EXP" ]; then bst_ok $S/probe-control-present else bst_notok $S/probe-control-present "$PROBE_EXP did not ship"; fi # One case per area rather than one for the whole 70-line blob: a red then points # at the area that failed instead of at the transcript, and the case list stays # stable when a line is added inside an area. cmp_group() { check_eq "$S/$1" \ "$(grep -E "^($2)[.=]" "$PROBE_EXP" 2>/dev/null)" \ "$(grep -E "^($2)[.=]" "$PROBE_OUT" 2>/dev/null)" } cmp_group probe-dist 'dist' cmp_group probe-version 'ver' cmp_group probe-matcher 'match' cmp_group probe-metadata 'meta' cmp_group probe-wheel 'wheel' cmp_group probe-no-network 'net' # The count is its own case: a probe that died halfway would otherwise match # every group it had already printed and report as a pass. cmp_group probe-case-count 'CASES' # THE MODULE'S OWN __version__ AGAINST THE RECIPE'S PIN, arm-pinned rather than # compared: the control runs the same tree, so a comparison could only ever # agree with itself. This is the case that catches a version bump landing # without the suite being re-pointed. check_eq $S/probe-version-pin "dist.version=$VER" \ "$(grep '^dist.version=' "$PROBE_OUT" 2>/dev/null)" rm -rf "$W"; rm -f "$W.pkgf" "$W.st"