Skip to content

FIX: Don't revert the entirety of sys.modules after running kernprof.main#437

Merged
Erotemic merged 3 commits into
pyutils:mainfrom
TTsangSC:436-fix
Jul 18, 2026
Merged

FIX: Don't revert the entirety of sys.modules after running kernprof.main#437
Erotemic merged 3 commits into
pyutils:mainfrom
TTsangSC:436-fix

Conversation

@TTsangSC

@TTsangSC TTsangSC commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Closes #436.

(EDIT: typos; missing summaries for kernprof.y::main() and line_profiler/autoprofile/autoprofile.py::run())

Code changes

  • line_profiler/line_profiler_utils.py::restore:
    • Migrated from kernprof.py::_restore, so that other parts of the codebase can use it for cleanup.
    • The constructor method .mapping() (resp. .instance_dict()) now takes an extra argument keys: Collection[K] (resp. attrs: Collection[str]) to limit the scope of reversion.
    • The context manager is now reentrant.
    • Minor backward incompatibility: instances no longer expose the attributes .getter, .setter, and .old. (But we don't really use it anywhere other than as anonymous decorators and context managers anyway, so nothing changed.)
  • kernprof.py::main():
    On exit:
    • Instead of freezing the entire sys.modules, we now only revert sys.modules['__main__'] or the lack thereof.
    • Instead of freezing the entire line_profiler._diagnostics namespace, we now only revert line_profiler._diagnostics.log which is overridden.
  • line_profiler/autoprofile/autoprofile.py::run():
    Instead of freezing the entire sys.modules when running the supplied code, we now only revert sys.modules['__main__'] or the lack thereof.

Test changes

  • line_profiler/line_profiler_utils.py::restore.sequence()'s doctest extended to demonstrate/check for reentrant behavior.
  • tests/test_kernprof.py:
    • Factored out common pattern of creating a tempdir and cd-ing therein into a helper context manager (chdir_temp).
    • Minor fix: test_kernprof_m_import_resolution() and test_kernprof_eager_preimport_bad_module() now use os.pathsep instead of a hard-coded ':' to join ${PYTHONPATH} components.
    • New test test_ppe_pickling formalizes the failing case in Reverting sys.modules causes pickling issues with concurrent.futures.ProcessPoolWorker #436, checking that repeated invocation of kernprof.main() running concurrent.futures code doesn't fail due to consistency issues with sys.modules.

TTsangSC added 2 commits July 18, 2026 08:59
tests/test_kernprof.py
    chdir_temp
        New helper context manager for creating a new tempdir and
        `cd`-ing into it, factored out from common pattern in tests
    test_*()
        Updated to use the above
    test_kernprof_m_import_resolution()
    test_kernprof_eager_preimport_bad_module()
        Replaced hard-coded ':' in the `${PYTHONPATH}` value with
        `os.pathsep`
    test_ppe_pickling()
        New test which uses `concurrent.futures.ProcessPoolExecutor` in
        code run by `kernprof.main()`; the case where PPE hasn't been
        imported prior to `kernprof.main()` being called twice currently
        fails
kernprof.py
    _restore
        Migrated to `line_profiler.line_profiler_utils.restore`
    main()
        Instead of reverting the entire module namespace of
        `line_profiler.diagnostics`, now only reverting
        `line_profiler.diagnostics.log` (which is replaced in
        `_parse_arguments()`)
    _main_profile()
        Instead of reverting the entire `sys.modules`, now only
        reverting `sys.modules['__main__']`

line_profiler/autoprofile/autoprofile.py::run()
    Replaced internal context manager with a `restore` instance

line_profiler/line_profiler_utils.py::restore
    - Migrated from `kernprof.py::_restore`
    - Added type annotations
    - Added optional argument `keys` (resp. `attrs`) to `.mapping()`
      (resp. `.instance_dict()`) so that users can selectively revert
      certain key-value pairs (resp. attributes)
    - Now reentrant
    - Note: the attributes `.setter`, `.getter`, and `.obj` are removed
@codecov

codecov Bot commented Jul 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.24561% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 82.76%. Comparing base (4940bca) to head (5845332).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
line_profiler/line_profiler_utils.py 98.18% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #437      +/-   ##
==========================================
+ Coverage   82.40%   82.76%   +0.36%     
==========================================
  Files          20       20              
  Lines        2256     2298      +42     
  Branches      359      361       +2     
==========================================
+ Hits         1859     1902      +43     
  Misses        300      300              
+ Partials       97       96       -1     
Files with missing lines Coverage Δ
line_profiler/autoprofile/autoprofile.py 90.90% <100.00%> (ø)
line_profiler/line_profiler_utils.py 96.05% <98.18%> (+9.09%) ⬆️

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f8e40f6...5845332. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Erotemic Erotemic left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all LGTM. Minor comments. You can choose what to do with them. Say the word and I'll merge.

Comment thread tests/test_kernprof.py Outdated


def main() -> None:
with ProcessPoolExecutor(mp_context=get_context('spawn')) as ex:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
with ProcessPoolExecutor(mp_context=get_context('spawn')) as ex:
with ProcessPoolExecutor(max_workers=2, mp_context=get_context('spawn')) as ex:

Not sure if we want to use all CPUs on a test. On one hand it could expose more edge cases on the other, setting it is more stable.

Comment thread kernprof.py Outdated
short_string_path,
)
from line_profiler.line_profiler_utils import (
restore as _restore, # Compatibility

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compatibility for a private name isn't required. It doesn't bother me though. This is mostly a nitpick.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, since we're technically already changing restore's API (it no longer has .getter, .setter, and .old – but then again those members aren't used anywhere by the code outside of the class itself). Might as well just import under the new name.

Comment thread tests/test_kernprof.py
python_path = str(temp_dpath)
if 'PYTHONPATH' in os.environ:
python_path += ':' + os.environ['PYTHONPATH']
python_path += os.pathsep + os.environ['PYTHONPATH']

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch

kernprof.py
    No longer importing `line_profiler.line_profiler_utils.restore`
    under the name `_restore` since it isn't fully compatible to the old
    `_restore` anyway

tests/test_kernprof.py::test_ppe_pickling()
    Now explicitly passing `max_workers` to PPE to avoid spinning up too
    many processes
@TTsangSC

Copy link
Copy Markdown
Collaborator Author

Thanks for the quick review, should be alright now (other than the ty lint – but that's a known issue, and #434 would patch pyproject.toml and fix that).

@Erotemic
Erotemic merged commit 7c1e41f into pyutils:main Jul 18, 2026
44 of 45 checks passed
@Erotemic

Copy link
Copy Markdown
Member

Yeah I ignored the ty error for that reason, let's land #434 next

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reverting sys.modules causes pickling issues with concurrent.futures.ProcessPoolWorker

2 participants