Repository navigation
[hotfix][java][python] Release skill repositories on any load failure and remove dead code - #951
Conversation
… and remove dead code SkillManager._load_skills released already-opened repositories only for OSError and ValueError. Any other failure, a corrupt archive raising zipfile.BadZipFile or a failure inside _register_repo, skipped the cleanup and leaked the repositories' temp directories until interpreter exit, because the caller never receives a SkillManager reference to close them itself. Wrap the source loop so the cleanup runs on every failure path, leaving unchanged which exceptions are wrapped in RuntimeError. Also remove SkillManager.resolveResourcePath and SkillRepositoryInfo, which have no callers. The resource-path method is deleted in both languages together so neither side is left without its counterpart.
|
The Python path now cleans up repositories on every load/registration failure, but the Java |
SkillManager.loadAll released already-opened repositories only inside its catch of IOException and IllegalArgumentException. Any other failure from open(), originOf(), or registerRepo() escaped the constructor without calling closeRepos(), so the repositories' temp directories and shutdown hooks leaked until JVM exit. The caller never receives a SkillManager reference on a failed construction, so it cannot release them itself. Wrap the source loop so the cleanup runs on every failure path. Which exceptions get wrapped in IllegalStateException is unchanged: IOException and IllegalArgumentException still wrap with the same message and cause, and everything else still propagates unchanged in type and identity. The guard accepts Throwable, and so does the handler around the cleanup call itself, because closeRepos() does not catch Error per repository and an Error escaping the cleanup would otherwise replace the original failure outright. This is the counterpart of the Python guard added alongside it.
|
Good catch, that gap was real. I reproduced it before fixing: an Fixed in 59344b0. The source loop is now wrapped in an outer guard that runs The handler around the cleanup call itself was also too narrow. Two regression tests. The first is your scenario:
|
Linked issue: none (hotfix)
Purpose of change
Two related cleanups in the skill module.
Repositories leaked on unexpected load failures.
SkillManagerreleased already-opened repositories only for a narrow set of exception types. Python's_load_skillsguardedOSErrorandValueError; Java'sloadAllguardedIOExceptionandIllegalArgumentException. Any other failure skipped the cleanup, so the repositories' temp directories and shutdown hooks leaked until interpreter or JVM exit. The caller never receives aSkillManagerreference on a failed construction, so it cannot clean them up itself. Concrete cases: a corrupt archive raiseszipfile.BadZipFile, which does not inherit fromOSError; a failure inside_register_repowas outside the guarded region entirely; and on the Java side an unexpectedRuntimeExceptionorErrorfromopen(),originOf(), orregisterRepo()escaped the constructor.The source loop is now wrapped in both languages so the cleanup runs on every failure path. Which exceptions get wrapped is unchanged: Python still wraps
OSErrorandValueErrorinRuntimeError, Java still wrapsIOExceptionandIllegalArgumentExceptioninIllegalStateException, both with the same message and cause chaining, and everything else still propagates unchanged in type and identity. The only behavior change is that the cleanup now runs.Java's guard takes
Throwable, and so does the handler around the cleanup call itself.closeRepos()does not catchErrorper repository andSkillRepository.close()declares no checked exception, so anErrorraised while releasing a repository would otherwise escape and replace the original failure with nothing suppressed.Dead code removed.
SkillManager.resolveResourcePathandSkillRepositoryInfohave no callers. The resource-path method existed in both languages, so both are deleted in the same commit rather than leaving one side without its counterpart. Its only reference was a Java test, which is removed with it.Tests
Four tests added, two per language, covering the previously unguarded paths.
Python
TestSkillManagerMixedSources: a source raisingzipfile.BadZipFilefromopen(), and a failure raised from_register_repoafter an earlier source loaded cleanly. Both assert the earlier repository was closed and that the original exception propagates unwrapped.Java
SkillManagerTest: a repository failing registration with anIllegalStateException, asserting both the earlier repository and the failing one were closed, that the original exception arrives by identity, and that a cleanup failure arrives as suppressed. Identity rather than type is the load-bearing assertion, sinceIllegalStateExceptionis also what the wrapping catch builds. A second test covers theErrorpath, which is what pins both handlers toThrowable.All four were verified to be discriminating rather than merely green. On the Python side, re-narrowing the handler so it no longer spans
_register_repofails only the registration test while the pre-existing cleanup tests stay green. On the Java side, narrowing either handler back toExceptionfails theErrortest, and restrictingaddSuppressedto the wrapped path fails both new tests while the pre-existing suppressed-cleanup test passes.Java
SkillManagerTest19 passed and the fullruntimemodule 402 passed. Python skill module 68 passed and the full suite 619 passed, 11 skipped.spotless:check,ruff check, andruff formatare all clean.API
No public API change.
SkillManageris a runtime-internal component, and both removed symbols had no callers in either language.Documentation
doc-neededdoc-not-neededdoc-included