mirror of
https://github.com/LorenEteval/Furious.git
synced 2026-09-22 23:08:08 +03:00
Document Qt lifetime boundaries
Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
+13
-6
@@ -224,7 +224,7 @@ Avoid unnecessary manual disconnect boilerplate when Qt already manages the conn
|
||||
|
||||
Native PySide6 and a Nuitka-compiled application do not necessarily have the same
|
||||
Python-callable retention graph. In the locally selected and inspected toolchain
|
||||
(Nuitka 4.1.3, PySide6 6.8.3), Nuitka's standard PySide6 package configuration patches
|
||||
(Nuitka 4.2.1, PySide6 6.8.3), Nuitka's standard PySide6 package configuration patches
|
||||
`SignalInstance.connect()` and `QTimer.singleShot()`. When the callback is a compiled
|
||||
bound method, the generated post-import code protects it in a process-global list named
|
||||
`_protected` and may also expose its underlying function on the receiver class. The
|
||||
@@ -233,10 +233,9 @@ attribute. This protection keeps the bound receiver strongly reachable. Repeated
|
||||
transient receivers can therefore grow for the whole packaged-process lifetime even
|
||||
when native CPython destroys them.
|
||||
|
||||
The same protection pattern exists in current upstream Nuitka source. Related PySide6
|
||||
workaround behavior is documented for earlier Nuitka/PySide6 combinations, but do not
|
||||
assume an exact introduction version without checking the selected release. Always
|
||||
inspect the package configuration installed in the environment being shipped.
|
||||
This observation is specific to the inspected local package configuration. Do not
|
||||
assume an exact introduction version or unchanged upstream behavior; inspect the
|
||||
package configuration installed in the environment being shipped.
|
||||
|
||||
The following is prohibited for a transient or repeatedly created receiver:
|
||||
|
||||
@@ -287,6 +286,14 @@ receiver itself is gone. When the sender is the receiver or a child/descendant,
|
||||
destruction already removes the connection and the extra destroyed-receiver hook is
|
||||
unnecessary. Supplying `sender=` still keeps only a weak sender reference.
|
||||
|
||||
That subtree check describes the ownership at registration time. When a child can be
|
||||
detached or reused, its feature must disconnect its own stored connection handles
|
||||
before reparenting and clear stale child references on native destruction.
|
||||
`AppQMessageBox` button registration and its detach/reuse tests cover this boundary.
|
||||
User callbacks can also synchronously destroy a sender or receiver during delivery;
|
||||
recheck native validity before further Qt work, as the HTTP completion and abort
|
||||
regressions in `test_service_runtime.py` demonstrate.
|
||||
|
||||
`singleShotWeakly(milliseconds, receiver, 'methodName')` uses the same named weak
|
||||
dispatcher without a sender. If the receiver wrapper is gone or its native QObject is
|
||||
invalid when the timer fires, delivery becomes a no-op.
|
||||
@@ -443,7 +450,7 @@ alone is not proof of no leak.
|
||||
|
||||
Treat the exact retention workaround as version-sensitive. The repository currently
|
||||
ships several PySide6 versions across platform artifacts, while the inspected local
|
||||
development combination is PySide6 6.8.3 with Nuitka 4.1.3. Re-inspect the selected
|
||||
development combination is PySide6 6.8.3 with Nuitka 4.2.1. Re-inspect the selected
|
||||
Nuitka package configuration and run the representative compiled probe when a release
|
||||
toolchain changes; do not generalize one combination's private `_protected` visibility
|
||||
or behavior to every native or packaged build.
|
||||
|
||||
@@ -47,6 +47,9 @@ behavior, and lifetime primitives; pages and services consume them without creat
|
||||
`singleShotWeakly()` for deferred named-method delivery. A closure/partial that captures the receiver does not
|
||||
substitute for weak dispatch. Bounded dialog-completion closures may intentionally capture context; verify their
|
||||
native destruction/disconnection boundary and both owner-first and sender-first teardown.
|
||||
- Detaching a child ends the shared QObject-tree lifetime assumption. Disconnect the registrations owned by that
|
||||
feature before reparenting, preserve unrelated listeners, and remove default/escape/selection references when the
|
||||
child dies. `AppQMessageBox` button reuse and native-destruction regressions exercise this boundary.
|
||||
- Direct connections are appropriate for deliberately shared persistent lifetimes; syntax alone does not prove a
|
||||
leak. Recheck the selected Nuitka/PySide6 callback protection when the toolchain changes. Static weak method names
|
||||
are runtime contracts, so renames must update registrations and tests. Weak dispatch itself does not marshal
|
||||
@@ -60,7 +63,9 @@ behavior, and lifetime primitives; pages and services consume them without creat
|
||||
- Every `QNetworkReply` has one manager/context owner, one freshness rule, and one terminal deletion path. Request
|
||||
context must also be released when native destruction skips `finished`, including manager-first teardown with
|
||||
retained Python wrappers. Use the shared network-manager tracking boundary; cleanup must not capture a reply
|
||||
strongly. Do not attach ad-hoc attributes to third-party Qt objects or multiply timers/connections across show/hide cycles.
|
||||
strongly. User hooks and signal delivery can synchronously destroy the reply or manager; recheck native validity
|
||||
before subsequent hooks or Qt cleanup. `test_service_runtime.py` covers completion and abort reentrancy.
|
||||
Do not attach ad-hoc attributes to third-party Qt objects or multiply timers/connections across show/hide cycles.
|
||||
- Queued delivery never transfers ownership implicitly. The sender may finish before delivery, so callbacks resolve a
|
||||
still-valid receiver and current generation in the receiver's Qt thread before touching widgets, models, or wrappers.
|
||||
|
||||
|
||||
+2
-2
@@ -52,7 +52,7 @@ strategy in an individual test.
|
||||
| Xray/Hysteria2 native-TUN ownership and proxy-only stripping | `test_native_tun_semantics.py` |
|
||||
| Rolling metrics, stable buckets, lazy rendering, and hover | `test_metrics_behavior.py` |
|
||||
| Proxy-only endpoint discovery, caching, and presentation | `test_endpoint_info.py` |
|
||||
| Bounded service work, update validation, plugin UI, and worker lifetime | `test_service_runtime.py` |
|
||||
| Bounded service work, update validation, plugin UI, worker lifetime, and reentrant HTTP completion/cancellation | `test_service_runtime.py` |
|
||||
| Frozenlib state helpers and mocked platform-operation boundaries | `test_frozenlib.py` |
|
||||
| Settings sandbox, navigation overlay behavior, public exports, and scale/theme isolation | `test_isolation_and_navigation.py`, `test_public_api.py`, `test_layout_matrix.py` |
|
||||
| Shared Fluent visual states, zero table padding and intact rounded list corners, rounded menu/combo popup transparency and interaction, and native line-edit clear-button alignment and theme changes | `test_stylesheet_states.py` |
|
||||
@@ -62,7 +62,7 @@ strategy in an individual test.
|
||||
| Editor mappings, lazy log rendering, routing-rule internal-move drag restrictions, Move menu/shortcuts and persisted order, message-box/connection UI | `test_ui_behavior.py` |
|
||||
| Bounded, incremental, cancellable QR export and snapshot/lifetime safety | `test_qr_export_scalability.py` |
|
||||
| Real keyboard/mouse/focus, proxy mapping, shared Home/Settings state, Home empty/filter recovery, shared menus and Tests-popup selection highlighting, direct small profile operations and batched imports/deletions with throttled progress and stable cancellation targets, and transient editor input | `test_qt_interactions.py` |
|
||||
| Direct Qt ownership and destruction across independent UI families | `test_qt_lifetime.py` |
|
||||
| Direct Qt ownership and destruction across independent UI families, message-box button detachment/reuse, and callback-driven owner destruction | `test_qt_lifetime.py` |
|
||||
| Batched real/probe Qt object, QR rendering/window lifecycle, handle, Python allocation, and RSS trends | `test_qt_stress.py` |
|
||||
| Repeated harmless subprocess, pipe, thread, handle, and RSS trends | `test_process_stress.py` |
|
||||
| Opt-in release-confidence counts (100 app children, 100 external cores, 100k metrics, 40k logs, 20k navigation, 5k plugins, 1k dialogs, 1k real QR tabs) | `test_very_heavy.py` |
|
||||
|
||||
Reference in New Issue
Block a user