• 
      

    Fix a crash when accessing locals on Python 3.13.

    Review Request #14168 — Created Sept. 13, 2024 and submitted

    Information

    kgb
    master

    Reviewers

    kgb

    Python 3.13 is introducing some behind-the-scenes changes for how local
    variables are accessed within a function, with an eye toward performance
    and reliability. This normally wouldn't impact users in any way, but it
    does impact kgb, given the work required to dynamically patch functions.

    The problem was that when spying on closures where the inner function
    references an argument from a parent function, any attempt to access
    locals() would fail. Since we need to store locals() in order to
    access variables in the function, this broke us.

    The reason it happens is a bit unclear, but the fix is not.

    Historically, when creating a new CodeType for a patched function, we
    would retain the co_freevars and co_cellvars of the old function,
    which was at some point necessary for closures. Best I can tell, this
    was needed when creating a new merged CodeType on these older
    releases, but does not seem to be important anymore (at least when using
    CodeType.replace()).

    Removing the overridden co_freevars and co_cellvars and using the
    new values only ended up fixing the locals() access. There doesn't
    seem to be a need to override these. I've tested with running the kgb,
    Review Board, Djblets, and RBTools test suites for all affected Python
    versions (3.8 through 3.13) without issue.

    Unit tests pass in kgb, Djblets, and Review Board with all supported
    versions of Python.

    Summary ID
    Fix a crash when accessing locals on Python 3.13.
    Python 3.13 is introducing some behind-the-scenes changes for how local variables are accessed within a function, with an eye toward performance and reliability. This normally wouldn't impact users in any way, but it does impact kgb, given the work required to dynamically patch functions. The problem was that when spying on closures where the inner function references an argument from a parent function, any attempt to access `locals()` would fail. Since we need to store `locals()` in order to access variables in the function, this broke us. The reason it happens is a bit unclear, but the fix is not. Historically, when creating a new `CodeType` for a patched function, we would retain the `co_freevars` and `co_cellvars` of the old function, which was at some point necessary for closures. Best I can tell, this was needed when creating a new merged `CodeType` on these older releases, but does not seem to be important anymore (at least when using `CodeType.replace()`). Removing the overridden `co_freevars` and `co_cellvars` and using the new values only ended up fixing the `locals()` access. There doesn't seem to be a need to override these. I've tested with running the kgb, Review Board, Djblets, and RBTools test suites for all affected Python versions (3.8 through 3.13) without issue.
    b1b63b23140e5a8a8669e9a1104ada18ba96cfb7
    david
    1. Ship It!
    2. 
        
    chipx86
    Review request changed
    Status:
    Completed
    Change Summary:
    Pushed to master (d1c01a9)