PyPI / filelock / 4.2.0

filelock 4.2.0

Source
https://github.com/tox-dev/py-filelock
Tag scanned
4.2.0
Compared with
4.1.2 (1 signals introduced since)
Files
93 scanned, 9 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 17 info
Scan time
14.6 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 17 of 17 warn and critical signals; 17 demoted to info.
Disclude version
4.0.0
Scanned (UTC)
2026-10-16 09:58

Signals

Ordered by severity, then signals introduced in this release. Hidden characters are shown as ⟨U+…⟩.

  1. info warn → info by review dynamic-attribute src/filelock/__init__.py:124:5 new in this release

    reach-by-name through `globals` (ast pass, confidence 0.60)

                value = getattr(module, f"Async{_FileLock.__name__}" if name == "AsyncFileLock" else name)
        globals()[name] = value
        return value
    likely benign LLM, confidence 0.90 A lazy module-attribute resolver in filelock's package __init__ that caches values in globals.
    Reasoning

    Assigning `globals()[name]` after a getattr on a module-level name is the standard lazy-attribute or PEP 562 style pattern for resolving names such as AsyncFileLock on demand. It is a normal library idiom with no payload or remote input.

  2. info warn → info by review dynamic-execution compose.yaml:18:30

    [embedded bash] command name is a variable (`$$`) not set in this file — the caller or environment decides what runs (ast pass, confidence 0.70)

    "cp -a /repo \"$$HOME/work\" && cd \"$$HOME/work\" && pip install -q . && exec tox run -e py -x env.py.base_python=3.14 -x env_pkg_base.default_base_python=3.14"
    likely benign LLM, confidence 0.70 A Compose service runs a bash one-liner that copies the repo, installs it, and runs tox.
    Reasoning

    The flagged `$$` is Compose's escape for a literal `$`, so `$$HOME` reaches the container shell as `$HOME`. The command names (cp, cd, pip, tox) are fixed, and the snippet is a standard dev or CI workflow for running tox in a copied workspace. The 'variable command name' reading is a misreading of the escaping. Only the surrounding file would confirm it, and that is not shown.

  3. info warn → info by review frame-introspection src/filelock/_api.py:1265:31

    `inspect.currentframe` — call-stack frame introspection (ast pass, confidence 0.70)

                    return
                state.releasing = inspect.currentframe()
                try:
                    _LOGGER.debug("Attempting to release lock %s on %s", id(self), self.lock_file)
    likely benign LLM, confidence 0.95 Records the current frame on the lock state while a release is in progress.
    Reasoning

    inspect.currentframe() is stored on the lock state to track the releasing caller for reentrancy and debug logging. This is ordinary library bookkeeping for a file-lock library.

  4. info warn → info by review frame-introspection src/filelock/_api.py:1407:35

    `inspect.currentframe` — call-stack frame introspection (ast pass, confidence 0.70)

            attempt = 0
            self._context.acquiring = inspect.currentframe()
            try:
                while True:
    likely benign LLM, confidence 0.95 Stores the current frame on the lock context during acquisition.
    Reasoning

    Saving the acquiring frame on the context object supports reentrancy and diagnostics in the lock acquisition path. It is legitimate introspection with no exfiltration or execution behavior.

  5. info warn → info by review frame-introspection src/filelock/_api.py:1661:14

    `inspect.currentframe` — call-stack frame introspection (ast pass, confidence 0.70)

            return False
        caller = inspect.currentframe()
        while caller is not None and caller is not frame:
            caller = caller.f_back
    likely benign LLM, confidence 0.95 Walks up the call stack to locate a specific frame.
    Reasoning

    The code walks the caller chain via f_back until it reaches a target frame. This is a standard way to find the calling frame for logging or ownership checks, and the walk is bounded.

  6. info warn → info by review frame-introspection tasks/capabilities.py:181:5

    `sys.setprofile` — call-stack frame introspection (ast pass, confidence 0.70)

        previous: Final = sys.getprofile()
        sys.setprofile(interrupt)
        try:
            with suppress(KeyboardInterrupt), contextmanager(probe)():
    likely benign LLM, confidence 0.80 Temporarily installs a profile hook to test interrupt handling, then restores the previous one.
    Reasoning

    This is a capability probe in the task scripts. It installs a profile hook that interrupts, runs a contextmanager under suppress(KeyboardInterrupt), and restores the previous profile in finally. That matches testing interrupt-safety of the lock code. It is unusual but consistent with the project's own test tooling.

  7. info warn → info by review frame-introspection tasks/capabilities.py:186:9

    `sys.setprofile` — call-stack frame introspection (ast pass, confidence 0.70)

        finally:
            sys.setprofile(previous)
        return bool(finished)
    likely benign LLM, confidence 0.80 Restores the previously installed profile function after the probe.
    Reasoning

    This restores the previous profile function in a finally block, which is cleanup for the probe above. No external effect is visible.

  8. info warn → info by review frame-introspection tasks/capabilities.py:229:13

    `sys.setprofile` — call-stack frame introspection (ast pass, confidence 0.70)

                nested.append(True)
                sys.setprofile(sys.getprofile())
    
        sys.addaudithook(setprofile_again)
    likely benign LLM, confidence 0.70 An audit-hook callback re-sets the profile function as part of a capability probe.
    Reasoning

    An audit hook re-installs the profile function using sys.getprofile() and a nested counter. This is somewhat unusual self-reinstalling behavior, but it reads as a probe of whether profile hooks persist across audit events, not a payload. The surrounding file is not shown, so confidence is moderate.

  9. info warn → info by review frame-introspection tasks/capabilities.py:233:9

    `sys.setprofile` — call-stack frame introspection (ast pass, confidence 0.70)

        try:
            sys.setprofile(sys.getprofile())
        except RuntimeError:
            return False
    likely benign LLM, confidence 0.80 Checks whether sys.setprofile can be called, returning False on RuntimeError.
    Reasoning

    This is a capability check that tries to set the profile function and returns False on RuntimeError. It is a feature-detection pattern with no harmful effect.

  10. info warn → info by review frame-introspection tests/test_filelock.py:1498:5

    `sys.setprofile` — call-stack frame introspection (ast pass, confidence 0.70)

        entered: Final[set[FrameType]] = set()
        sys.setprofile(interrupt)
        try:
            lock.release()
    likely benign LLM, confidence 0.90 A test installs a profile hook to inject an interrupt during lock release.
    Reasoning

    The test installs a profile hook that interrupts, calls lock.release() under it, and records entered frames. This tests signal or interrupt safety of the release path, which is standard test technique.

  11. info warn → info by review frame-introspection tests/test_filelock.py:1504:9

    `sys.setprofile` — call-stack frame introspection (ast pass, confidence 0.70)

        finally:
            sys.setprofile(None)
        return False
    likely benign LLM, confidence 0.90 Clears the profile hook after an interrupt-injection test.
    Reasoning

    The finally block clears the profile hook after the test. This is test cleanup.

  12. info warn → info by review frame-introspection tests/test_soft_lease.py:401:9

    `sys.setprofile` — call-stack frame introspection (ast pass, confidence 0.70)

        def read_compromise() -> None:
            sys.setprofile(pause_in_claim_creation)
            try:
                assert lease.compromise is None
    likely benign LLM, confidence 0.90 A test installs a profile hook to pause during claim creation to exercise a race.
    Reasoning

    The test sets a profile hook to pause during claim creation so it can check the race with read_compromise. This is deterministic concurrency testing in a test file.

  13. info warn → info by review frame-introspection tests/test_soft_lease.py:405:13

    `sys.setprofile` — call-stack frame introspection (ast pass, confidence 0.70)

            finally:
                sys.setprofile(None)
    
        reader: Final = Thread(target=read_compromise)
    likely benign LLM, confidence 0.90 Clears the profile hook after a lease race test.
    Reasoning

    The finally block clears the profile hook after the race test. This is test cleanup.

  14. info warn → info by review frame-introspection tests/test_soft_lifetime_warning.py:47:28

    `sys._getframe` — call-stack frame introspection (ast pass, confidence 0.70)

            if entry_point == "constructor":
                warning_line = sys._getframe().f_lineno + 1
                lock_type(tmp_path / "test.lock", lifetime=10)
            else:
    likely benign LLM, confidence 0.95 Reads the current line number to check where a lifetime warning is reported.
    Reasoning

    sys._getframe().f_lineno is used in a test to compute the expected warning line number. This is a common way to assert warning locations.

  15. info warn → info by review frame-introspection tests/test_soft_lifetime_warning.py:51:28

    `sys._getframe` — call-stack frame introspection (ast pass, confidence 0.70)

                lock = lock_type(tmp_path / "test.lock")
                warning_line = sys._getframe().f_lineno + 1
                lock.lifetime = 10
    likely benign LLM, confidence 0.95 Reads the current line number to check a lifetime warning location.
    Reasoning

    The same pattern as the previous finding: a test reads its own line number to assert the warning location.

  16. info warn → info by review frame-introspection tests/test_soft_lifetime_warning.py:139:28

    `sys._getframe` — call-stack frame introspection (ast pass, confidence 0.70)

            if entry_point == "constructor":
                warning_line = sys._getframe().f_lineno + 1
                lock_type(tmp_path / "test.lock", lifetime=10)
            else:
    likely benign LLM, confidence 0.95 Reads the current line number to check a lifetime warning location.
    Reasoning

    Again a test reading its own line number to verify warning attribution. No malicious behavior.

  17. info warn → info by review frame-introspection tests/test_soft_lifetime_warning.py:143:28

    `sys._getframe` — call-stack frame introspection (ast pass, confidence 0.70)

                lock = lock_type(tmp_path / "test.lock")
                warning_line = sys._getframe().f_lineno + 1
                lock.lifetime = 10
    likely benign LLM, confidence 0.93 A test uses sys._getframe() to capture its own line number for asserting warning locations.
    Reasoning

    sys._getframe().f_lineno is used in a test to record the current line number so the test can assert that a warning points at the correct line. This is a common, benign testing technique; the frame introspection is local and not used for evasion, exfiltration, or dynamic execution.

Reproduce This Scan

Run disclude locally to verify. Add --format sarif for SARIF output. Add --llm (with an API key in the environment) to include the LLM review.

cargo install disclude --version 4.0.0
git clone https://github.com/tox-dev/py-filelock py-filelock && cd py-filelock
git checkout 4.2.0
disclude scan --severity info --diff 4.1.2 .