. "$BST_LIB" # BST-TIMEOUT: 1200 # THREE python3 starts, and they are the bulk of the cost. # # Everything else here reads the filesystem or dpkg with # # shell builtins, or greps the output of one of the three. # # # # They are a bare `import', a second one whose STDERR is # # the assertion (`import-quiet'), and the probe -- which # # takes ONE start for fifty-nine measurements. # # # # THE PROBE ALSO SLEEPS, which no other python3 suite here # # does, and the arithmetic is worth writing down. Nine # # blocks must OVERRUN a one-second deadline to be measured # # at all, and each sleeps 1.6 s, so the probe spends about # # 14.4 s waiting however fast the machine is. That is the # # floor of what this package can be tested at: a deadline # # that is not exceeded proves nothing about a timeout. # # # # 1200 is 300 s per start plus that floor, the same budget # # 99-python3-arrow and 99-python3-aniso8601 set for their # # starts, and roughly five times the ~60 s a python3 start # # has measured at on this arm. # # # # A suite-timeout red is a batch-wide cost, so if the three # # starts prove slower than that on target the RIGHT fix is # # this number, not fewer cases. # # # # NOT MEASURED ON TARGET YET: this port's first on-target # # run is the batch verify, and the measured elapsed # # replaces this note in the test plan rather than in this # # comment, which would cost an image build to keep the # # shipped suite byte-identical. # # python3-stopit 1.1.2 (meta-openembedded/meta-python # recipes-devtools/python/python3-stopit_1.1.2.bb, `inherit pypi setuptools3', # PyPI sdist) on the wasm32-hwjs glibc image. # # ============================================================================ # WHAT THE PACKAGE DOES. # ============================================================================ # # It ends a block that has run too long. Two context managers -- `SignalTimeout' # and `ThreadingTimeout' -- and the two decorators built on them take a deadline # in seconds, run the block, and either let it finish or interrupt it with a # `TimeoutException'. A five-value `state' attribute and a `__bool__' say which # happened. # # Four pure-Python files, no C, no compile step, no CLI, no data. That purity is # what makes it portable here at all: 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 whatever it built to. # `pkg-no-so' and `pkg-only-python' assert that from the packaged file list. # # ============================================================================ # ONLY THE SIGNAL FLAVOUR CAN INTERRUPT A BLOCK, AND ONLY ON A KERNEL WITH THE # brintos/linux#77 FIX. THE THREAD FLAVOUR CANNOT ON ANY KERNEL, AND THAT IS # WHERE THIS FILE'S EXPECTED REDS COME FROM. # ============================================================================ # # SignalTimeout installs a SIGALRM handler and calls `signal.alarm(n)'. # On a kernel with the brintos/linux#77 fix alarm(2) is # wired and the SIGALRM reaches the handler (measured on # hz1, 2026-09-16/17). On a kernel without it alarm(2) is # ENOSYS, and since `signal.alarm()' RAISES NOTHING the # whole flavour fails silently. The same fix is what # makes mosquitto's `-W' (88-mosquitto) fire. # # ThreadingTimeout arms a `threading.Timer' and posts the exception into the # caller from it. `Thread.start()' itself raises # RuntimeError here (brintos/linux-in-the-browser#85), so # no timer thread is ever created and the timer body never # runs. That is not stopit's defect. 99-python3.test # already pins it as `python3/thread-start-refused'; # `thread-runs' below re-measures it inside this probe so # a timeout red can be read against the mechanism rather # than guessed at. # # TWO KINDS OF CASE FOLLOW FROM THAT, AND THEY ARE DELIBERATELY DIFFERENT. # # THE MECHANISM CASES ASSERT WHAT THIS PLATFORM DOES: `ctypes-import', # `async-raise-bad-tid', `sigalrm-delivered', `alarm-returns' and # `thread-runs'. One of them -- `thread-runs' -- goes GREEN while the # platform is broken. It is a RED WITNESS in the sense 99-python3.test uses -- # it fails on the day its issue closes, which is the day someone should be # re-reading this file. It is not a claim that the platform is right. # `ctypes-import' and `async-raise-bad-tid' were witnesses of the same kind # until _ctypes was built in (BRINTOS-2269); they now carry the control's # values, measured on this arm. # The other two, `alarm-returns' and `sigalrm-delivered', assert the WORKING # signal path, the values a control gives. On a kernel with the # brintos/linux#77 fix a red on either is a regression, and the SignalTimeout # cases red with it; on a kernel pin without the fix all of them read red by # design. # # THE PACKAGE-CONTRACT CASES ASSERT WHAT stopit DOCUMENTS: every `*-over-*', # `deco-*-slow' and `nest-inner-state' case. A block that overran its # deadline must report TIMED_OUT and be falsy. The SignalTimeout ones # (`sig-over-*', `deco-sig-slow', `deco-named-slow', `nest-inner-state') are # expected GREEN on a kernel with the brintos/linux#77 fix. The # ThreadingTimeout ones (`thr-over-*', `deco-thr-slow') are EXPECTED TO RED # on any kernel: the block reports EXECUTED and is truthy, which # is the answer a caller would read as success. Writing them the other way # round -- asserting the broken value -- would leave the suite green on a # flavour that never once ended a block, and would need editing on the day # the platform is fixed. That is the same choice 88-mosquitto and 84-qperf # made for the same reason. # # EVERY RED THIS FILE IS EXPECTED TO CARRY IS THEREFORE ATTRIBUTED IN ADVANCE, # to brintos/linux-in-the-browser#85 (the thread flavour). None of them is a # surprise and none of them is this recipe's. A red in a SignalTimeout case, or # on either signal witness, is a regression on a kernel with the # brintos/linux#77 fix; on a kernel pin without it those eight cases read red # by design. `sigalrm-delivered' says which kernel the run had, and whether it # was the platform or stopit that moved. # # ============================================================================ # IT WOULD NOT IMPORT AT ALL UNPATCHED, WHICH IS A THIRD AND SEPARATE GAP. # ============================================================================ # # `stopit/__init__.py' imports `.threadstop' unconditionally and that module did # `import ctypes' at module scope. `_ctypes' was not built into this interpreter # before BRINTOS-2269, so `import stopit' raised `ModuleNotFoundError' and # NOTHING in the package was reachable, the signal half included. The bbappend # carries a patch moving that import into `async_raise()', the package's only # user of it. With _ctypes built in the patch is no longer load-bearing, but it # still ships and is still pinned. `patch-lazy-ctypes', # `patch-no-module-scope-ctypes' and `no-ctypes-at-import' pin all three halves # of that -- the shipped source, the absent module-scope import, and the import # closure that proves it is not reached. # # ============================================================================ # TWO THINGS UPSTREAM DOES THAT A CALLER WILL NOT EXPECT. # ============================================================================ # # `cancel()' IS ERASED BY `__exit__'. Calling it inside the block sets `state' # to CANCELED (4), and then `__exit__' -- seeing no exception -- overwrites it # with EXECUTED (0). So the documented "I do not need the limit any more" state # is never observable from outside the block. `sig-cancel-state' asserts 0, the # measured answer, rather than the 4 the docstring implies. # # `TimeoutException' IS NOT IN `__all__'. It is the type every caller must # catch to use `swallow_exc=False', and `from stopit import *' does not bring # it. `exc-in-all' asserts the omission. # # NEITHER IS A brintOS DEFECT -- both reproduce on an x86-64 control -- so # neither is filed. They are asserted because they are the shapes a caller on # this image will meet. # # ONE MORE, IN THE METADATA RATHER THAN THE CODE: `setup.py' declares # `license="GPLv3"' while its own classifier says `License :: OSI Approved :: # MIT License' and the LICENSE file the recipe checksums is MIT. The recipe # takes MIT, which is what the LICENSE file and the classifier say; # `dist-info-license-gplv3' and `dist-info-classifier-mit' pin BOTH strings, so # the disagreement is recorded rather than tidied away. # # ============================================================================ # WHERE THE EXPECTED VALUES CAME FROM. # ============================================================================ # # Every literal below was MEASURED on an x86-64 control -- CPython 3.12 against # the image's 3.12 -- by running data/python3-stopit-probe.py against the tree # unpacked from python3-stopit_1.1.2-r0_wasm32.deb, built at recipe scope on hz1 # 2026-09-08 (1641 of 1641 tasks, zero ERROR: lines). The packaging literals # come from that same .deb's control file and file list. # # ONE MECHANISM CASE IS THE EXCEPTION, and it is stated as such: `thread-runs' # carries this ARM's value, taken from what 99-python3.test already measures on # this image, not from the control -- where it answers the other way. # `ctypes-import' and `async-raise-bad-tid' carry the control's values, which # this arm matches since _ctypes was built in (measured, BRINTOS-2269 verify # on HardwareJS 389ac7e). `alarm-returns' and # `sigalrm-delivered' carry the control's values, which this arm matches on a # kernel with the brintos/linux#77 fix (measured on hz1, 2026-09-16/17). # # ============================================================================ # UPSTREAM'S OWN TESTS ARE NOT HERE, AND THERE IS NO -ptest PACKAGE. # ============================================================================ # # `setup.py' names `test_suite="tests.suite"', but `MANIFEST.in' ships only # `src/', so the PyPI sdist this recipe fetches carries no `tests/' directory at # all -- there is nothing to install even if someone wanted it. And unlike most # meta-python recipes this one does not `inherit ptest', so there is no # `${PN}-ptest' to suppress either. `pkg-no-ptest' and `no-tests-in-site- # packages' assert both. P=python3-stopit bst_ok $P/suite-canary require python3 $P/python3-present SITE=/usr/lib/python3.12/site-packages PKGDIR=$SITE/stopit DI=$SITE/stopit-1.1.2.dist-info PKGLIST=/var/lib/dpkg/info/python3-stopit.list PROBE=$BST_DATA/python3-stopit-probe.py tmp=${TMPDIR:-/tmp} W=$tmp/bst-stopit.$$ mkdir -p "$W" || { bst_notok $P/workdir "cannot create $W"; exit 0; } # ------------------------------------------------------------ what shipped --- check $P/site-packages-dir test -d "$SITE" check $P/package-dir test -d "$PKGDIR" check $P/no-module-py test ! -f "$SITE/stopit.py" check $P/init-file test -s "$PKGDIR/__init__.py" check $P/utils-py test -s "$PKGDIR/utils.py" check $P/signalstop-py test -s "$PKGDIR/signalstop.py" check $P/threadstop-py test -s "$PKGDIR/threadstop.py" check $P/pycache test -s "$PKGDIR/__pycache__/__init__.cpython-312.pyc" check $P/no-cli test ! -e /usr/bin/stopit # NO PEP 561 MARKER. stopit ships no `py.typed', so a consumer's mypy reads no # annotations from it -- asserted because its absence is what lets # `pkg-only-python' below allow nothing but .py, .pyc and dist-info. check $P/no-py-typed test ! -e "$PKGDIR/py.typed" # UPSTREAM'S TESTS ARE NOT HERE -- see the header; they are not in the sdist. check $P/no-tests-in-site-packages test ! -d "$PKGDIR/tests" # ------------------------------------------------- THE PATCH IS IN THE FILE --- # The port's one patch, asserted on the SHIPPED source rather than on the # recipe. `import ctypes' must appear exactly once, indented, inside # `async_raise()' -- and never at module scope, which is what took the whole # package off this image. check_eq $P/patch-lazy-ctypes "1" \ "$(grep -c '^ import ctypes$' "$PKGDIR/threadstop.py" 2>/dev/null)" check_eq $P/patch-no-module-scope-ctypes "" \ "$(grep '^import ctypes$' "$PKGDIR/threadstop.py" 2>/dev/null)" cat "$PKGLIST" > "$W/pkgf" 2>/dev/null || : > "$W/pkgf" check $P/pkg-list-present test -s "$W/pkgf" check_eq $P/pkg-no-so "" \ "$(grep -E '\.so($|\.)' "$W/pkgf" 2>/dev/null)" # EVERY SHIPPED FILE IS PYTHON, its bytecode or its dist-info. A compiled # extension could not be imported on this arm at all (BRINTOS-1476), so this is # the packaging-side statement of what `no-so-in-closure' says from inside the # interpreter. _bad="" while IFS= read -r _f; do [ -f "$_f" ] || continue case "$_f" in *.py|*.pyc) continue ;; */__pycache__/*) continue ;; */*.dist-info/*) continue ;; esac _bad="$_bad $_f" done < "$W/pkgf" check_eq $P/pkg-only-python "" "$_bad" check $P/dist-info-dir test -d "$DI" check $P/dist-info-metadata test -f "$DI/METADATA" check_out $P/dist-info-name "Name: stopit" cat "$DI/METADATA" check_out $P/dist-info-version "Version: 1.1.2" cat "$DI/METADATA" check_out $P/dist-info-summary \ "Summary: Timeout control decorator and context managers, raise any exception in another thread" \ cat "$DI/METADATA" # THE LICENCE DISAGREES WITH ITSELF UPSTREAM -- see the header. Both strings are # pinned so the disagreement is recorded rather than tidied away. check_out $P/dist-info-license-gplv3 "License: GPLv3" cat "$DI/METADATA" check_out $P/dist-info-classifier-mit \ "Classifier: License :: OSI Approved :: MIT License" cat "$DI/METADATA" check_out $P/dist-info-top-level "stopit" cat "$DI/top_level.txt" # THE WHEEL WAS BUILT BY setuptools' bdist_wheel, which is what `setuptools3' # reduces to here -- the case that says the build class did its job. check_out $P/dist-info-generator "Generator: bdist_wheel" cat "$DI/WHEEL" check_out $P/dist-info-purelib "Root-Is-Purelib: true" cat "$DI/WHEEL" # NO LICENCE FILE IS INSTALLED. The recipe checksums a `file://LICENSE' it # fetches beside the sdist, but the sdist's own MANIFEST does not ship one into # the wheel, so the dist-info carries none. check $P/no-dist-info-licence test ! -e "$DI/LICENSE" # ------------------------------------------------------------- the package --- sed -n '/^Package: python3-stopit$/,/^$/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" grep '^Depends:' "$W/st" > "$W/dep" 2>/dev/null || : > "$W/dep" check $P/pkg-depends-line test -s "$W/dep" # THE WHOLE Depends LINE, BY NAME, IN BOTH DIRECTIONS. The bbappend replaces the # one entry meta-python declares and adds the two it omits; these five cases are # what turn that file's reasoning into something a release can red. check_out $P/pkg-depends-core "python3-core" cat "$W/dep" check_out $P/pkg-depends-ctypes "python3-ctypes" cat "$W/dep" check_out $P/pkg-depends-logging "python3-logging" cat "$W/dep" check_out $P/pkg-depends-pkg-resources "python3-pkg-resources" cat "$W/dep" # `python3-setuptools' IS GONE. stopit imports `pkg_resources', which poky ships # as its own package; it imports `setuptools' nowhere. The grep is anchored so # it cannot match the `python3-pkg-resources' entry above. check_eq $P/pkg-no-depends-setuptools "" \ "$(grep -o 'python3-setuptools' "$W/dep" 2>/dev/null)" # NO -ptest PACKAGE IS BUILT -- the recipe does not `inherit ptest' at all. check_eq $P/pkg-no-ptest "" \ "$(sed -n '/^Package: python3-stopit-ptest$/p' /var/lib/dpkg/status 2>/dev/null)" # --------------------------------------------------------------- it imports --- # THE CASE THE WHOLE PORT TURNS ON. Unpatched this is a ModuleNotFoundError out # of `threadstop''s module-scope `import ctypes', and every case below it is # unscorable. check $P/import python3 -c 'import stopit' # ...AND SAYS NOTHING WHILE DOING IT. `import stopit' reaches `pkg_resources', # which is deprecated upstream and warns in some configurations; a warning here # would land on a caller's stderr on every start. check_eq $P/import-quiet "" \ "$(python3 -c 'import stopit' 2>&1)" if [ ! -f "$PROBE" ]; then bst_notok $P/probe-present "$PROBE did not ship" rm -rf "$W" exit 0 fi bst_ok $P/probe-present bst_probe $P/probe-run "$W/probe" 900 python3 "$PROBE" OUT="$W/probe" pv() { grep "^$1=" "$OUT" 2>/dev/null | head -n 1 | cut -d= -f2-; } if [ -s "$OUT" ]; then bst_ok $P/probe-output else bst_notok $P/probe-output "the probe emitted nothing -- python3 did not start, or died before its first line; every CASE below is unscorable" fi check_eq $P/probe-complete "yes" "$(pv probe_done)" check_eq $P/probe-count "59" "$(pv emitted_count)" # ---------------------------------------------------------------- identity --- # THE MODULE THE PROBE MEASURED IS THE PACKAGED ONE. The probe sits in # ${libdir}/brintos-tests/data and the interpreter puts a script's own directory # first on sys.path, so this is the case that would catch a probe that had # shadowed the module it measures. check_eq $P/module-file "$PKGDIR/__init__.py" "$(pv module_file)" check_eq $P/is-package "yes" "$(pv is_package)" check_eq $P/submodules "signalstop,threadstop,utils" "$(pv submodules)" # BOTH VERSIONS, AND THEY COME FROM DIFFERENT PLACES. `__version__' is resolved # at import through `pkg_resources.get_distribution()' inside a bare `except:' # that falls back to the string `unknown', so a dist-info that failed to ship # would leave this green-looking and wrong; `dist-version' asks the metadata # directly and `version-attr' asks the module. check_eq $P/version-attr "1.1.2" "$(pv version_attr)" check_eq $P/dist-version "1.1.2" "$(pv dist_version)" # -------------------------------------------------------------- the API --- check_eq $P/all-names \ "SignalTimeout,ThreadingTimeout,async_raise,signal_timeoutable,threading_timeoutable" \ "$(pv all_names)" check_eq $P/all-count "5" "$(pv all_count)" check_eq $P/api-missing "" "$(pv api_missing)" check_eq $P/exc-name "TimeoutException" "$(pv exc_name)" check_eq $P/exc-base "Exception" "$(pv exc_base)" # `TimeoutException' IS NOT EXPORTED -- see the header. It is the type a caller # using `swallow_exc=False' must catch, and `from stopit import *' omits it. check_eq $P/exc-in-all "no" "$(pv exc_in_all)" # --------------------------------------------------------- the state model --- check_eq $P/states "0,1,2,3,4" "$(pv states)" check_eq $P/repr-fresh "" "$(pv repr_fresh)" check_eq $P/bool-fresh "True" "$(pv bool_fresh)" # THE TWO FLAVOURS TAKE THEIR DEADLINE DIFFERENTLY, and the asymmetry is a trap # rather than a detail: `SignalTimeout' truncates to an integer because alarm(2) # is integral, so `SignalTimeout(0.5)' is `alarm(0)' -- which CANCELS an alarm # and can never fire. `ThreadingTimeout' keeps the float. check_eq $P/sig-int-seconds "1" "$(pv sig_int_seconds)" check_eq $P/thr-float-seconds "1.9" "$(pv thr_float_seconds)" # --------------------------------------------- the two interrupt mechanisms --- # ONE VALUE BELOW IS THIS ARM'S, NOT THE CONTROL'S: `thread-runs'. It FAILS ON # THE DAY ITS ISSUE CLOSES -- which is the day the ThreadingTimeout reds further # down may turn green and this file should be re-read. The rest are the # control's. # # `import ctypes' succeeds: _ctypes is built into this interpreter since # BRINTOS-2269. This was a red witness for its absence until then. check_eq $P/ctypes-import "ok" "$(pv ctypes_import)" # ...so `async_raise()' reaches PyThreadState_SetAsyncExc through # `ctypes.pythonapi' (the main-program handle), which answers 0 for a thread id # that cannot exist, and the function raises ValueError -- the control's answer. check_eq $P/async-raise-bad-tid "" "$(pv async_raise_bad_tid)" # INSTALLING A SIGALRM HANDLER WORKS on either kernel. Without the # brintos/linux#77 fix it is delivery that fails, not registration, which is why # that failure is silent. check_eq $P/sigalrm-installable "yes" "$(pv sigalrm_installable)" # `alarm(2)' RETURNS 0, as on a control: no alarm was pending. On a kernel with # the brintos/linux#77 fix a -1 here is a regression; on a kernel pin without # the fix alarm(2) is ENOSYS, and this case reads -1, red by design. check_eq $P/alarm-returns "0" "$(pv alarm_returns)" # THE SIGALRM IS DELIVERED inside the 1.6 s sleep, on a kernel with the # brintos/linux#77 fix. Every SignalTimeout case below rests on this one fact, # so a `no' here reds them all: a regression on a kernel with the fix, and red # by design on a kernel pin without it. check_eq $P/sigalrm-delivered "yes" "$(pv sigalrm_delivered)" # AND A THREAD CANNOT BE STARTED AT ALL: `Thread.start()' itself raises # RuntimeError (brintos/linux-in-the-browser#85). This is the single fact that # reds every ThreadingTimeout case below. A RED WITNESS -- it reads `RAN' on the # day #85 closes, and `IDLE' if the start ever starts succeeding without the # body running, which is the state this file used to describe. check_eq $P/thread-runs "REFUSED" "$(pv thread_runs)" # -------------------------------------------- SignalTimeout: inside the bound --- # THE GREEN HALF OF THE PORT. A block that finishes before its deadline is never # interrupted on any platform, so these pass here exactly as on a control. check_eq $P/sig-inside-state "0" "$(pv sig_inside_state)" check_eq $P/sig-inside-bool "True" "$(pv sig_inside_bool)" check_eq $P/sig-state-in-block "1" "$(pv sig_state_in_block)" # `cancel()' IS ERASED BY `__exit__' -- see the header. The measured answer is # EXECUTED (0), not CANCELED (4). check_eq $P/sig-cancel-state "0" "$(pv sig_cancel_state)" check_eq $P/sig-cancel-bool "True" "$(pv sig_cancel_bool)" # --------------------------------------------- SignalTimeout: past the bound --- # EXPECTED GREEN on a kernel with the brintos/linux#77 fix. The block sleeps # 1.6 s under a 1 s deadline. Documented: TIMED_OUT (2) and falsy. On a kernel # without the fix the SIGALRM never arrives, the block runs to completion, and # the context manager reports EXECUTED (0) and truthy -- which a caller reads as # success. There that red is by design; on a kernel with the fix it is a # regression. check_eq $P/sig-over-state "2" "$(pv sig_over_state)" check_eq $P/sig-over-bool "False" "$(pv sig_over_bool)" # ...and with `swallow_exc=False' the exception must reach the caller. Without # the fix nothing is raised at all. check_eq $P/sig-over-noswallow "TimeoutException" "$(pv sig_over_noswallow)" # ------------------------------------------ ThreadingTimeout: inside the bound --- # ENTERING THE CONTEXT MANAGER MUST NOT RAISE. `setup_interrupt' calls # `Timer.start()', which on this arm RAISES RuntimeError, as `thread-runs' # shows (the kernel refuses the thread-mode clone with EAGAIN). EXPECTED RED # (brintos/linux-in-the-browser#85): entry fails, and `thr-inside-*', every # `thr-over-*' and `deco-thr-fast'/`deco-thr-slow' red with it as # `' -- for that reason, not for the timeout, and not for ctypes, # which nothing reaches before the timer fires. check_eq $P/thr-enter "ok" "$(pv thr_enter)" check_eq $P/thr-inside-state "0" "$(pv thr_inside_state)" check_eq $P/thr-inside-bool "True" "$(pv thr_inside_bool)" # ------------------------------------------- ThreadingTimeout: past the bound --- # EXPECTED RED (brintos/linux-in-the-browser#85). The same assertions the # signal flavour passes on a kernel with the brintos/linux#77 fix: the timer # thread cannot be started, so the block raises RuntimeError at entry and # nothing calls `async_raise'. check_eq $P/thr-over-state "2" "$(pv thr_over_state)" check_eq $P/thr-over-bool "False" "$(pv thr_over_bool)" check_eq $P/thr-over-noswallow "TimeoutException" "$(pv thr_over_noswallow)" # -------------------------------------------------------------- decorators --- # The decorator wraps the callable in the same context manager with # `swallow_exc=True' and returns its `default' when the block did not complete. # THE FAST AND NO-TIMEOUT CASES ARE GREEN HERE for the signal flavour: the # first finishes inside its deadline, and the second takes a different branch # entirely -- `if timeout:' is false, so the callable runs unwrapped with no # timeout machinery at all. `deco-thr-fast' is EXPECTED RED # (brintos/linux-in-the-browser#85): its Timer cannot start, see `thr-enter'. check_eq $P/deco-sig-fast "DONE" "$(pv deco_sig_fast)" check_eq $P/deco-sig-none "DONE" "$(pv deco_sig_none)" check_eq $P/deco-thr-fast "DONE" "$(pv deco_thr_fast)" check_eq $P/deco-thr-none "DONE" "$(pv deco_thr_none)" check_eq $P/deco-preserves-name "yes" "$(pv deco_preserves_name)" # The callable overruns and its `default' must come back instead of its return # value. `deco-sig-slow' is expected GREEN on a kernel with the # brintos/linux#77 fix; `deco-thr-slow' is EXPECTED RED # (brintos/linux-in-the-browser#85), for the reasons above. check_eq $P/deco-sig-slow "TIMEDOUT" "$(pv deco_sig_slow)" check_eq $P/deco-thr-slow "TIMEDOUT" "$(pv deco_thr_slow)" # ...INCLUDING UNDER A RENAMED PARAMETER. `timeout_param' exists because the # injected `timeout' keyword would otherwise collide with a callable that has # one of its own; this case is the one that would notice the rename being # ignored, which would look identical to a timeout that did not fire. check_eq $P/deco-named-slow "TIMEDOUT" "$(pv deco_named_slow)" # ----------------------------------------------------------------- nesting --- # AN INNER DEADLINE EXPIRING MUST NOT END THE OUTER BLOCK. The inner manager # swallows its own TimeoutException, so the outer block continues and reports # EXECUTED. The outer half alone would also be green if the inner never fired # at all -- as it does not on a kernel without the brintos/linux#77 fix -- # which is exactly why `nest-inner-state' is asserted beside it rather than the # outer alone. check_eq $P/nest-after-inner "reached" "$(pv nest_after_inner)" check_eq $P/nest-outer-state "0" "$(pv nest_outer_state)" # Expected GREEN on a kernel with the brintos/linux#77 fix. A red means the # inner deadline did not fire: a regression on a kernel with the fix, red by # design on a kernel pin without it. check_eq $P/nest-inner-state "2" "$(pv nest_inner_state)" # -------------------------------------------------------- no thread is left --- # A TIMEOUT MUST NOT LEAK A THREAD. `ThreadingTimeout' arms a `threading.Timer' # -- itself a thread -- and cancels it on exit. Scored as a DELTA, so it says # the same thing on a platform that starts threads and on one that does not: # either way, nothing may be left behind by the eleven blocks run above. check_eq $P/threads-leaked "0" "$(pv threads_leaked)" check_eq $P/threads-after "$(pv threads_before)" "$(pv threads_after)" # ----------------------------------------------------------- import closure --- # WHAT `import stopit' REACHES, scored against a snapshot taken before it. These # are the runtime half of the Depends cases above. check_eq $P/needs-pkg-resources "yes" "$(pv needs_pkg_resources)" check_eq $P/needs-logging "yes" "$(pv needs_logging)" check_eq $P/needs-threading "yes" "$(pv needs_threading)" check_eq $P/needs-signal "yes" "$(pv needs_signal)" # ...AND THE ONE IT DOES NOT. `setuptools' is what meta-python's RDEPENDS named. check_eq $P/no-setuptools "yes" "$(pv no_setuptools)" # `ctypes' MUST NOT BE IN THE IMPORT CLOSURE. That is the whole point of the # patch this port carries: a module-scope import coming back would take # `import stopit' off this image with it, and this case is what would say so # before the `import' case above turned red for a reason nobody could see. check_eq $P/no-ctypes-at-import "yes" "$(pv no_ctypes_at_import)" check_eq $P/python-major-minor "3.12" "$(pv python_major_minor)" rm -rf "$W" 2>/dev/null