diff --git a/AGENTS.md b/AGENTS.md index 1c6ac2d2..c5688233 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -93,7 +93,8 @@ changing public exports, plugin APIs, persisted keys/schemas, IDs, aliases, migrations, package data, or semantic exit codes. Prefer module-scope imports, grouped as Furious, PySide6, built-in `from` imports, then bare imports; use `ex` for - caught exception variables. Preserve verified deferred-import boundaries for lazy discovery, import cycles, and + caught exception variables. Put `# Any non-exit exceptions` directly below every `except Exception` handler. + Preserve verified deferred-import boundaries for lazy discovery, import cycles, and child-only native bindings. Separate logical code phases with blank lines without splitting one cohesive operation. - Generated and curated artifacts have separate sources of truth: never hand-edit `Furious/Frozenlib/AppResources.py`; update `Resources.qrc`/resource inputs and regenerate it. Follow diff --git a/Furious/Core/SingTUN.py b/Furious/Core/SingTUN.py index 174de3cf..4e987e92 100644 --- a/Furious/Core/SingTUN.py +++ b/Furious/Core/SingTUN.py @@ -342,6 +342,8 @@ class SingTUN(MultiprocessingRuntime): try: super().stop() except Exception as ex: + # Any non-exit exceptions + errors.append(str(ex)) # A stopped process is not proof of host restoration. Failure keeps the @@ -355,12 +357,16 @@ class SingTUN(MultiprocessingRuntime): # DNS snapshots can be restored independently. self._hostPlan.finishDNSCleanup() except Exception as ex: + # Any non-exit exceptions + errors.append(str(ex)) if callable(self.cleanup): try: self.cleanup() except Exception as ex: + # Any non-exit exceptions + errors.append(str(ex)) if errors: diff --git a/Furious/Repository/SingTUNSettings.py b/Furious/Repository/SingTUNSettings.py index a27ab298..de9485fa 100644 --- a/Furious/Repository/SingTUNSettings.py +++ b/Furious/Repository/SingTUNSettings.py @@ -58,6 +58,8 @@ class UserSingTUNSettings(Mixins.CleanupOnExit, StorageBackend): self._data = data except Exception: + # Any non-exit exceptions + self._restoreFailed = True logger.exception('failed to restore persisted sing-tun settings') diff --git a/Furious/Service/ConnectionManager.py b/Furious/Service/ConnectionManager.py index 8b0700ca..26400b96 100644 --- a/Furious/Service/ConnectionManager.py +++ b/Furious/Service/ConnectionManager.py @@ -885,6 +885,8 @@ class ConnectionStartOperation(QtCore.QObject): self._waitSingHost('_startSingTUN') except Exception as ex: + # Any non-exit exceptions + self._fail('Failed to prepare sing-tun', str(ex)) def _waitSingHost(self, continuation): @@ -1746,6 +1748,8 @@ class ConnectionManager(Mixins.CleanupOnExit): return True except Exception as ex: + # Any non-exit exceptions + self._lastStartError = str(ex) return attempt.rollback('sing-tun startup failed: ' + self._lastStartError) diff --git a/Furious/Service/SingTUNHost.py b/Furious/Service/SingTUNHost.py index 28826104..0e464331 100644 --- a/Furious/Service/SingTUNHost.py +++ b/Furious/Service/SingTUNHost.py @@ -283,6 +283,8 @@ class SingTUNHostPlan: if not self._cancel.is_set(): getattr(self, method)(*args) except Exception as ex: + # Any non-exit exceptions + self.error = str(ex) finally: self._done.set() @@ -643,6 +645,8 @@ class SingTUNHostPlan: self._dnsRestore.remove((platform, snapshot)) except Exception as ex: + # Any non-exit exceptions + errors.append(str(ex)) if errors: @@ -655,12 +659,16 @@ class SingTUNHostPlan: try: self.restoreDNS() except Exception as ex: + # Any non-exit exceptions + errors.append(str(ex)) if self._activated: try: self._recoverNative() except Exception as ex: + # Any non-exit exceptions + errors.append(str(ex)) if errors: diff --git a/tests/test_log_manager_generation.py b/tests/test_log_manager_generation.py index c9d359d8..bba4a3b5 100644 --- a/tests/test_log_manager_generation.py +++ b/tests/test_log_manager_generation.py @@ -461,6 +461,8 @@ class GenerationLogManagerContractTest(unittest.TestCase): _assertManagerInvariants(self, manager, model) except Exception as error: + # Any non-exit exceptions + self.fail( f'seed={seed} operation={operationIndex} error={error!r} ' f'history={history!r}' @@ -1349,6 +1351,8 @@ class GenerationLogManagerContractTest(unittest.TestCase): try: manager.append('racing', CORE_LOG_CATEGORY) except Exception as error: + # Any non-exit exceptions + errors.append(error) producer = threading.Thread(target=append) @@ -1411,6 +1415,8 @@ class GenerationLogManagerContractTest(unittest.TestCase): with sequenceLock: returnedSequences.append(entry.sequence) except Exception as error: + # Any non-exit exceptions + errors.append(error) def reader(): @@ -1444,6 +1450,8 @@ class GenerationLogManagerContractTest(unittest.TestCase): cursor = batch.cursor manager.entryCount(CORE_LOG_CATEGORY) except Exception as error: + # Any non-exit exceptions + errors.append(error) def mutator(): @@ -1464,6 +1472,8 @@ class GenerationLogManagerContractTest(unittest.TestCase): else: manager.snapshot(CORE_LOG_CATEGORY) except Exception as error: + # Any non-exit exceptions + errors.append(error) threads = [ diff --git a/tests/test_models_and_services.py b/tests/test_models_and_services.py index 9e91a804..b5e92aa6 100644 --- a/tests/test_models_and_services.py +++ b/tests/test_models_and_services.py @@ -1196,6 +1196,8 @@ class LogManagerTest(unittest.TestCase): categoryIds[index % len(categoryIds)], ) except Exception as error: + # Any non-exit exceptions + failures.append(error) finally: finished.set() @@ -1214,6 +1216,8 @@ class LogManagerTest(unittest.TestCase): if finished.wait(0.0001): break except Exception as error: + # Any non-exit exceptions + failures.append(error) observer = threading.Thread(target=observeAndClear)