14
0

- Update to 5.0.0:

* ENH: Add support for sys.monitoring (Python >= 3.12)
  * FIX: Fixed issue when calling kernprof with neither the -l nor -b flag
  * FIX: Fixed auto-profiling of async function definitions
  * ENH: Added CLI argument -m to kernprof for running a library module as
    a script
  * FIX: Fixed explicit profiling of class methods; added handling for
    profiling static, bound, and partial methods, functools.partial objects,
    (cached) properties, and async generator functions
  * FIX: Fixed namespace bug when running kernprof -m on certain modules.
  * FIX: Fixed @contextlib.contextmanager bug where the cleanup code (e.g.
    restoration of sys attributes) is not run if exceptions occurred inside
    the context
  * ENH: Added CLI arguments -c to kernprof for (auto-)profiling
    module/package/inline-script execution instead of that of script files;
    passing '-' as the script-file name now also reads from and profiles
    stdin
  * ENH: In Python >=3.11, profiled objects are reported using their
    qualified name.
  * ENH: Highlight final summary using rich if enabled
  * ENH: Made it possible to use multiple profiler instances simultaneously
  * ENH: various improvements related to auto-profiling:
  * FIX: Fixed line tracing for Cython code; superseded use of the legacy
    tracing system with sys.monitoring
  * FIX: Tracing-system-related fixes
  * ENH: Added capability to parse TOML config files for defaults
- Add patch no-python-in-path.patch:
  * Do not search the path for python.
- Add patch support-python314.patch:
  * Support Python 3.14 sys.monitoring changes.

OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-line_profiler?expand=0&rev=33
This commit is contained in:
2025-11-05 03:33:41 +00:00
committed by Git OBS Bridge
commit 83c7a1f81c
8 changed files with 678 additions and 0 deletions

23
.gitattributes vendored Normal file
View File

@@ -0,0 +1,23 @@
## Default LFS
*.7z filter=lfs diff=lfs merge=lfs -text
*.bsp filter=lfs diff=lfs merge=lfs -text
*.bz2 filter=lfs diff=lfs merge=lfs -text
*.gem filter=lfs diff=lfs merge=lfs -text
*.gz filter=lfs diff=lfs merge=lfs -text
*.jar filter=lfs diff=lfs merge=lfs -text
*.lz filter=lfs diff=lfs merge=lfs -text
*.lzma filter=lfs diff=lfs merge=lfs -text
*.obscpio filter=lfs diff=lfs merge=lfs -text
*.oxt filter=lfs diff=lfs merge=lfs -text
*.pdf filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.rpm filter=lfs diff=lfs merge=lfs -text
*.tbz filter=lfs diff=lfs merge=lfs -text
*.tbz2 filter=lfs diff=lfs merge=lfs -text
*.tgz filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text
*.txz filter=lfs diff=lfs merge=lfs -text
*.whl filter=lfs diff=lfs merge=lfs -text
*.xz filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.zst filter=lfs diff=lfs merge=lfs -text

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.osc

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:09e10f25f876514380b3faee6de93fb0c228abba85820ba1a591ddb3eb451a96
size 199037

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a80f0afb05ba0d275d9dddc5ff97eab637471167ff3e66dcc7d135755059398c
size 376919

46
no-python-in-path.patch Normal file
View File

@@ -0,0 +1,46 @@
Index: line_profiler-5.0.0/line_profiler/cli_utils.py
===================================================================
--- line_profiler-5.0.0.orig/line_profiler/cli_utils.py
+++ line_profiler-5.0.0/line_profiler/cli_utils.py
@@ -188,9 +188,7 @@ def get_python_executable():
Command or path thereto corresponding to
:py:data:`sys.executable`.
"""
- if os.path.samefile(shutil.which('python'), sys.executable):
- return 'python'
- elif os.path.samefile(shutil.which('python3'), sys.executable):
+ if os.path.samefile(shutil.which('python3'), sys.executable):
return 'python3'
else:
return short_string_path(sys.executable)
Index: line_profiler-5.0.0/tests/test_kernprof.py
===================================================================
--- line_profiler-5.0.0.orig/tests/test_kernprof.py
+++ line_profiler-5.0.0/tests/test_kernprof.py
@@ -184,7 +184,7 @@ def test_kernprof_sys_restoration(capsys
{'^Output to stdout': True,
r"^Wrote .* '.*script\.py\.lprof'": True,
r'^Inspect results with:''\n'
- r'python -m line_profiler .*script\.py\.lprof': True,
+ r'.*python.* -m line_profiler .*script\.py\.lprof': True,
r'line_profiler\.autoprofile\.autoprofile'
r'\.run\(\n(?:.+,\n)*.*\)': False,
r'^\[kernprof .*\]': False,
@@ -194,7 +194,7 @@ def test_kernprof_sys_restoration(capsys
{'^Output to stdout': True,
r"^Wrote .* '.*script\.py\.lprof'": True,
r'^Inspect results with:''\n'
- r'python -m line_profiler .*script\.py\.lprof': False,
+ r'.*python.* -m line_profiler .*script\.py\.lprof': False,
r'line_profiler\.autoprofile\.autoprofile'
r'\.run\(\n(?:.+,\n)*.*\)': False,
r'^\[kernprof .*\]': False,
@@ -204,7 +204,7 @@ def test_kernprof_sys_restoration(capsys
{'^Output to stdout': True,
r"^\[kernprof .*\] Wrote .* '.*script\.py\.lprof'": True,
r'Inspect results with:''\n'
- r'python -m line_profiler .*script\.py\.lprof': False,
+ r'.*python.* -m line_profiler .*script\.py\.lprof': False,
r'line_profiler\.autoprofile\.autoprofile'
r'\.run\(\n(?:.+,\n)*.*\)': True,
r'^Function: main': True},

View File

@@ -0,0 +1,248 @@
-------------------------------------------------------------------
Wed Nov 5 03:33:11 UTC 2025 - Steve Kowalik <steven.kowalik@suse.com>
- Update to 5.0.0:
* ENH: Add support for sys.monitoring (Python >= 3.12)
* FIX: Fixed issue when calling kernprof with neither the -l nor -b flag
* FIX: Fixed auto-profiling of async function definitions
* ENH: Added CLI argument -m to kernprof for running a library module as
a script
* FIX: Fixed explicit profiling of class methods; added handling for
profiling static, bound, and partial methods, functools.partial objects,
(cached) properties, and async generator functions
* FIX: Fixed namespace bug when running kernprof -m on certain modules.
* FIX: Fixed @contextlib.contextmanager bug where the cleanup code (e.g.
restoration of sys attributes) is not run if exceptions occurred inside
the context
* ENH: Added CLI arguments -c to kernprof for (auto-)profiling
module/package/inline-script execution instead of that of script files;
passing '-' as the script-file name now also reads from and profiles
stdin
* ENH: In Python >=3.11, profiled objects are reported using their
qualified name.
* ENH: Highlight final summary using rich if enabled
* ENH: Made it possible to use multiple profiler instances simultaneously
* ENH: various improvements related to auto-profiling:
* FIX: Fixed line tracing for Cython code; superseded use of the legacy
tracing system with sys.monitoring
* FIX: Tracing-system-related fixes
* ENH: Added capability to parse TOML config files for defaults
- Add patch no-python-in-path.patch:
* Do not search the path for python.
- Add patch support-python314.patch:
* Support Python 3.14 sys.monitoring changes.
-------------------------------------------------------------------
Sun May 4 08:46:41 UTC 2025 - Dirk Müller <dmueller@suse.com>
- update to 4.2.0:
* FIX: Fix issue with auto-profile of editable installs #279
* FIX: Lookup OP-codes instead of hard coding them #284
* CHANGE: Drop support for Python 3.6 and Python 3.7
* ENH: Add support for Python 3.13
-------------------------------------------------------------------
Tue Nov 26 23:59:21 UTC 2024 - Steve Kowalik <steven.kowalik@suse.com>
- Update to 4.1.3:
* FIX: duration summary now respects the stripzeros argument.
* FIX: minor test fixes.
* ENH: building osx wheels for x86 and arm64.
* ENH: documentation improvements.
* Invoke subshell with the current python interpreter
* Respect stripzeros in summary report
* Normalize path before comparison
- Switch to pyproject macros.
- Drop patch use-sys-executable-python.patch, included upstream.
-------------------------------------------------------------------
Wed Mar 13 15:29:56 UTC 2024 - Dirk Müller <dmueller@suse.com>
- skip python 3.9 build
-------------------------------------------------------------------
Tue Dec 5 12:55:42 UTC 2023 - Dirk Müller <dmueller@suse.com>
- update to 4.1.2:
* ENH: Add support for Python 3.12 #246
* ENH: Add osx universal2 and arm64 wheels
* ENH: Fix issue with integer overflow on 32 bit systems
* FIX: ``get_stats`` is no longer slowed down when profiling
many code sections #236
* FIX: skipzeros now checks for zero hits instead of zero time
* FIX: Fixed errors in Python 3.11 with duplicate functions.
* FIX: ``show_text`` now increases column sizes or switches to
scientific notation to maintain alignment
* ENH: ``show_text`` now has new options: sort and summarize
* ENH: Added new CLI arguments ``-srm`` to ``line_profiler`` to
control sorting, rich printing, and summary printing.
* ENH: New global ``profile`` function that can be enabled by
``--profile`` or ``LINE_PROFILE=1``.
* ENH: New auto-profile feature in ``kernprof`` that will
profile all functions in specified modules.
* ENH: Kernprof now outputs instructions on how to view
results.
* ENH: Added readthedocs integration:
https://kernprof.readthedocs.io/en/latest/index.html
- Add patch use-sys-executable-python.patch:
* Use sys.executable, rather than 'python'.
-------------------------------------------------------------------
Sun Aug 13 21:37:39 UTC 2023 - Dirk Müller <dmueller@suse.com>
- restrict to older Cython release
-------------------------------------------------------------------
Sun Mar 26 19:55:08 UTC 2023 - Dirk Müller <dmueller@suse.com>
- update to 4.0.3:
* FIX: Stop requiring bleeding-edge Cython unless necesasry
(for Python 3.12). #206
-------------------------------------------------------------------
Mon Feb 20 07:01:56 UTC 2023 - Steve Kowalik <steven.kowalik@suse.com>
- Update to 4.0.2:
* FIX: AttributeError on certain methods. #191
* FIX: Profiling classmethods works again. #183
* ENH: Python 3.11 is now supported.
* ENH: Profiling overhead is now drastically smaller, thanks to
reimplementing almost all of the tracing callback in C++.
* ENH: Added the ``-i <# of seconds>`` option to the ``kernprof`` script.
* CHANGE: Cython's native cythonize function is now used to compile the
project, instead of scikit-build's convoluted process.
* CHANGE: Due to optimizations done while reimplementing the callback in
C++, the profiler's code_map and last_time attributes now are indexed by
a hash of the code block's bytecode and its line number.
* FIX: filepath test in is_ipython_kernel_cell for Windows #161
* ADD: setup.py now checks LINE_PROFILER_BUILD_METHOD to determine how
to build binaries
* ADD: LineProfiler.add_function warns if an added function has a
__wrapped__ attribute
- Due to build system change, massively simplify %build.
-------------------------------------------------------------------
Thu Sep 29 14:50:17 UTC 2022 - Yogalakshmi Arunachalam <yarunachalam@suse.com>
- Update to Version 3.5.2
* FIX: filepath test in is_ipython_kernel_cell for Windows #161
* ADD: setup.py now checks LINE_PROFILER_BUILD_METHOD to determine how to build binaries
* ADD: LineProfiler.add_function warns if an added function has a __wrapped__ attribute
- Update to Version 3.5.1
* FIX: #19 line profiler now works on async functions again
- Update to Version 3.5.0
* FIX: #109 kernprof fails to write to stdout if stdout was replaced
* FIX: Fixes max of an empty sequence error #118
* Make IPython optional
* FIX: #100 Exception raise ZeroDivisionError
-------------------------------------------------------------------
Fri Jan 7 09:54:01 UTC 2022 - Ben Greiner <code@bnavigator.de>
- Update to 3.4.0
* Drop support for Python <= 3.5.x
* FIX: #104 issue with new IPython kernels
* Wheels for musllinux are now included
* FIX: Fix bug where lines were not displayed in Jupyter>=6.0 via
#93
- Release 3.3.1
* CHANGE: moving forward, new pypi releases will be signed with
the GPG key 2A290272C174D28EA9CA48E9D7224DAF0347B114 for
PyUtils-CI openpyutils@gmail.com. For reference, older versions
were signed with either
262A1DF005BE5D2D5210237C85CD61514641325F or
1636DAF294BA22B89DBB354374F166CFA2F39C18.
- Release 3.2.5
* Include c source files in manifest (#74)
- Fix IPython requirements
- Fix platform install dir
-------------------------------------------------------------------
Thu Apr 22 12:59:14 UTC 2021 - Markéta Machová <mmachova@suse.com>
- update to 3.1.0
* Restructure into package
* fix Python 3.9
-------------------------------------------------------------------
Wed Jan 2 12:35:35 UTC 2019 - Tomáš Chvátal <tchvatal@suse.com>
- Regenerate cython files to fix build under python 3.7
-------------------------------------------------------------------
Mon Oct 29 15:21:25 UTC 2018 - Todd R <toddrme2178@gmail.com>
- It doesn't have a direct dependency on prompt_toolkit.
-------------------------------------------------------------------
Thu Jul 19 15:06:24 UTC 2018 - mcepl@suse.com
- Switch off tests (they are broken,
https://github.com/rkern/line_profiler/issues/128)
-------------------------------------------------------------------
Thu May 10 15:23:01 UTC 2018 - toddrme2178@gmail.com
- Set minimum python3 version
-------------------------------------------------------------------
Thu May 3 14:07:08 UTC 2018 - toddrme2178@gmail.com
- Use %license tag
-------------------------------------------------------------------
Thu Apr 19 17:16:36 UTC 2018 - toddrme2178@gmail.com
- Update to version 2.1.2
* ENH: Add support for Python 3.5 coroutines
* ENH: Documentation updates
* ENH: CI for most recent Python versions (3.5, 3.6, 3.6-dev, 3.7-dev, nightly)
* ENH: Add timer unit argument for output time granularity spec
-------------------------------------------------------------------
Tue Aug 29 21:23:54 UTC 2017 - toddrme2178@gmail.com
- Update to version 2.0
* Added support for IPython 5.0+, removed support for IPython <=0.12
- Update to version 1.1
* Read source files as bytes.
- Implement single-spec version
-------------------------------------------------------------------
Tue Nov 11 16:08:30 UTC 2014 - toddrme2178@gmail.com
- Implement update-alternatives
- Implement unit tests
-------------------------------------------------------------------
Thu Oct 2 17:50:42 UTC 2014 - termim@gmail.com
- Update to 1.0
* `kernprof.py` is now installed as `kernprof`.
* Python 3 support. Thanks to the long-suffering Mikhail Korobov for
being patient.
* Dropped 2.6 as it was too annoying.
* The `stripzeros` and `add_module` options. Thanks to Erik Tollerud
for contributing it.
* Support for IPython cell blocks. Thanks to Michael Forbes for adding
this feature.
* Better warnings when building without Cython. Thanks to David
Cournapeau for spotting this.
-------------------------------------------------------------------
Mon Mar 10 15:05:50 UTC 2014 - toddrme2178@gmail.com
- Update to 1.0b3
* Profile generators.
* Update for compatibility with newer versions of Cython.
Thanks to Ondrej Certik for spotting the bug.
* Update IPython compatibility for 0.11+. Thanks to Yaroslav
Halchenko and others for providing the updated imports.
-------------------------------------------------------------------
Fri Jul 1 08:10:03 UTC 2011 - saschpe@suse.de
- Initial version

96
python-line_profiler.spec Normal file
View File

@@ -0,0 +1,96 @@
#
# spec file for package python-line_profiler
#
# Copyright (c) 2025 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
# Please submit bugfixes or comments via https://bugs.opensuse.org/
#
%{?sle15_python_module_pythons}
Name: python-line_profiler
Version: 5.0.0
Release: 0
Summary: Line-by-line profiler
License: BSD-3-Clause
URL: https://github.com/pyutils/line_profiler
Source: https://files.pythonhosted.org/packages/source/l/line_profiler/line_profiler-%{version}.tar.gz
# PATCH-FIX-OPENSUSE We do not ship bare python, so don't look for it
Patch0: no-python-in-path.patch
# PATCH-FIX-UPSTREAM gh#pyutils/line_profiler#369
Patch1: support-python314.patch
BuildRequires: %{python_module Cython}
BuildRequires: %{python_module devel}
BuildRequires: %{python_module ipython}
BuildRequires: %{python_module pip}
BuildRequires: %{python_module pytest}
BuildRequires: %{python_module scikit-build}
BuildRequires: %{python_module setuptools}
BuildRequires: %{python_module ubelt}
BuildRequires: %{python_module wheel}
BuildRequires: cmake
BuildRequires: fdupes
BuildRequires: gcc-c++
BuildRequires: ninja
BuildRequires: python-rpm-macros
Requires: python-ipython
Requires(post): update-alternatives
Requires(postun): update-alternatives
%python_subpackages
%description
line_profiler will profile the time individual lines of code take to execute.
The profiler is implemented in C via Cython in order to reduce the overhead of
profiling.
Also included is the script kernprof.py which can be used to conveniently
profile Python applications and scripts either with line_profiler or with the
function-level profiling tools in the Python standard library.
%prep
%autosetup -p1 -n line_profiler-%{version}
%build
export CFLAGS="%{optflags} -fno-strict-aliasing"
# remove shebangs
sed -i '1{/env python/d}' line_profiler/line_profiler.py kernprof.py
%pyproject_wheel
%install
%pyproject_install
%python_clone -a %{buildroot}%{_bindir}/kernprof
%python_compileall
%python_expand %fdupes %{buildroot}%{$python_sitearch}
%post
%python_install_alternative kernprof
%postun
%python_uninstall_alternative kernprof
%check
export PATH=%{buildroot}%{_bindir}:$PATH
mv line_profiler line_profiler-do-not-import
# cython_examples not shipped
%pytest_arch -k 'not cython_source'
mv line_profiler-do-not-import line_profiler
%files %{python_files}
%doc README.rst
%license LICENSE.txt LICENSE_Python.txt
%python_alternative %{_bindir}/kernprof
%{python_sitearch}/line_profiler
%{python_sitearch}/line_profiler-%{version}.dist-info
%{python_sitearch}/kernprof.py
%pycache_only %{python_sitearch}/__pycache__/kernprof.*.pyc
%changelog

258
support-python314.patch Normal file
View File

@@ -0,0 +1,258 @@
From 0b32904897bff5d91886cf2476e3bb98638cb31e Mon Sep 17 00:00:00 2001
From: joncrall <erotemic@gmail.com>
Date: Tue, 29 Jul 2025 18:35:33 -0400
Subject: [PATCH 1/9] Update xcookie
---
.github/workflows/tests.yml | 46 ++++++++++++++++++++++++-------------
docs/source/conf.py | 6 +++--
2 files changed, 34 insertions(+), 18 deletions(-)
Index: line_profiler-5.0.0/pyproject.toml
===================================================================
--- line_profiler-5.0.0.orig/pyproject.toml
+++ line_profiler-5.0.0/pyproject.toml
@@ -30,7 +30,7 @@ omit =[
]
[tool.cibuildwheel]
-build = "cp38-* cp39-* cp310-* cp311-* cp312-* cp313-*"
+build = "cp38-* cp39-* cp310-* cp311-* cp312-* cp313-* cp314-*"
skip = ["*-win32", "cp313-musllinux_i686"]
build-frontend = "build"
build-verbosity = 1
Index: line_profiler-5.0.0/requirements/build.txt
===================================================================
--- line_profiler-5.0.0.orig/requirements/build.txt
+++ line_profiler-5.0.0/requirements/build.txt
@@ -7,7 +7,7 @@ scikit-build>=0.11.1
cmake>=3.21.2
ninja>=1.10.2
-cibuildwheel>=2.11.2 ; python_version < '4.0' and python_version >= '3.11' # Python 3.11+
-cibuildwheel>=2.11.2 ; python_version < '3.11' and python_version >= '3.10' # Python 3.10
-cibuildwheel>=2.11.2 ; python_version < '3.10' and python_version >= '3.9' # Python 3.9
-cibuildwheel>=2.11.2 ; python_version < '3.9' and python_version >= '3.8' # Python 3.8
+cibuildwheel>=3.1.2 ; python_version < '4.0' and python_version >= '3.11' # Python 3.11+
+cibuildwheel>=3.1.2 ; python_version < '3.11' and python_version >= '3.10' # Python 3.10
+cibuildwheel>=3.1.2 ; python_version < '3.10' and python_version >= '3.9' # Python 3.9
+cibuildwheel>=3.1.2 ; python_version < '3.9' and python_version >= '3.8' # Python 3.8
Index: line_profiler-5.0.0/setup.py
===================================================================
--- line_profiler-5.0.0.orig/setup.py
+++ line_profiler-5.0.0/setup.py
@@ -304,6 +304,7 @@ if __name__ == '__main__':
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
+ 'Programming Language :: Python :: 3.14',
'Programming Language :: Python :: Implementation :: CPython',
'Topic :: Software Development',
]
Index: line_profiler-5.0.0/line_profiler/_line_profiler.pyx
===================================================================
--- line_profiler-5.0.0.orig/line_profiler/_line_profiler.pyx
+++ line_profiler-5.0.0/line_profiler/_line_profiler.pyx
@@ -260,7 +260,7 @@ cpdef _code_replace(func, co_code):
code = func.__func__.__code__
if hasattr(code, 'replace'):
# python 3.8+
- code = code.replace(co_code=co_code)
+ code = _copy_local_sysmon_events(code, code.replace(co_code=co_code))
else:
# python <3.8
co = code
@@ -273,6 +273,30 @@ cpdef _code_replace(func, co_code):
return code
+cpdef _copy_local_sysmon_events(old_code, new_code):
+ """
+ Copy the local events from ``old_code`` over to ``new_code`` where
+ appropriate.
+
+ Returns:
+ code: ``new_code``
+ """
+ try:
+ mon = sys.monitoring
+ except AttributeError: # Python < 3.12
+ return new_code
+ # Tool ids are integers in the range 0 to 5 inclusive.
+ # https://docs.python.org/3/library/sys.monitoring.html#tool-identifiers
+ NUM_TOOLS = 6
+ for tool_id in range(NUM_TOOLS):
+ try:
+ events = mon.get_local_events(tool_id, old_code)
+ mon.set_local_events(tool_id, new_code, events)
+ except ValueError: # Tool ID not in use
+ pass
+ return new_code
+
+
cpdef int _patch_events(int events, int before, int after):
"""
Patch ``events`` based on the differences between ``before`` and
@@ -370,22 +394,26 @@ cdef class _SysMonitoringState:
mon = sys.monitoring
# Set prior state
+ # Note: in 3.14.0a1+, calling `sys.monitoring.free_tool_id()`
+ # also calls `.clear_tool_id()`, causing existing callbacks and
+ # code-object-local events to be wiped... so don't call free.
+ # this does have the side effect of not overriding the active
+ # profiling tool name if one is already in use, but it's
+ # probably better this way
self.name = mon.get_tool(self.tool_id)
if self.name is None:
self.events = mon.events.NO_EVENTS
+ mon.use_tool_id(self.tool_id, 'line_profiler')
else:
self.events = mon.get_events(self.tool_id)
- mon.free_tool_id(self.tool_id)
- mon.use_tool_id(self.tool_id, 'line_profiler')
mon.set_events(self.tool_id, self.events | self.line_tracing_events)
- # Register tracebacks
- for event_id, callback in [
- (mon.events.LINE, handle_line),
- (mon.events.PY_RETURN, handle_return),
- (mon.events.PY_YIELD, handle_yield),
- (mon.events.RAISE, handle_raise),
- (mon.events.RERAISE, handle_reraise)]:
+ # Register tracebacks and remember the existing ones
+ for event_id, callback in [(mon.events.LINE, handle_line),
+ (mon.events.PY_RETURN, handle_return),
+ (mon.events.PY_YIELD, handle_yield),
+ (mon.events.RAISE, handle_raise),
+ (mon.events.RERAISE, handle_reraise)]:
self.callbacks[event_id] = mon.register_callback(
self.tool_id, event_id, callback)
@@ -394,12 +422,11 @@ cdef class _SysMonitoringState:
cdef dict wrapped_callbacks = self.callbacks
# Restore prior state
- mon.free_tool_id(self.tool_id)
- if self.name is not None:
- mon.use_tool_id(self.tool_id, self.name)
- mon.set_events(self.tool_id, self.events)
- self.name = None
- self.events = mon.events.NO_EVENTS
+ mon.set_events(self.tool_id, self.events)
+ if self.name is None:
+ mon.free_tool_id(self.tool_id)
+ self.name = None
+ self.events = mon.events.NO_EVENTS
# Reset tracebacks
while wrapped_callbacks:
@@ -1118,7 +1145,7 @@ datamodel.html#user-defined-functions
# function (on some instance);
# (re-)pad with no-op
co_code = base_co_code + NOP_BYTES * npad
- code = _code_replace(func, co_code=co_code)
+ code = _code_replace(func, co_code)
try:
func.__code__ = code
except AttributeError as e:
@@ -1155,6 +1182,9 @@ datamodel.html#user-defined-functions
code_hashes.append(code_hash)
# We can't replace the code object on Cython functions, but
# we can *store* a copy with the correct metadata
+ # Note: we don't use `_copy_local_sysmon_events()` here
+ # because Cython shim code objects don't support local
+ # events
code = code.replace(co_filename=cython_source)
profilers_to_update = {self}
# Update `._c_code_map` and `.code_hash_map` with the new line
Index: line_profiler-5.0.0/tests/test_sys_monitoring.py
===================================================================
--- line_profiler-5.0.0.orig/tests/test_sys_monitoring.py
+++ line_profiler-5.0.0/tests/test_sys_monitoring.py
@@ -6,7 +6,7 @@ from functools import partial
from io import StringIO
from itertools import count
from types import CodeType, ModuleType
-from typing import (Any, Optional, Union,
+from typing import (Any, Optional, Union, Literal,
Callable, Generator,
Dict, FrozenSet, Tuple,
ClassVar)
@@ -754,3 +754,76 @@ def _test_callback_toggle_local_events_h
assert get_loop_hits() == nloop_before_disabling + nloop_after_reenabling
return n
+
+
+@pytest.mark.parametrize('profile_when', ['before', 'after'])
+def test_local_event_preservation(
+ profile_when: Literal['before', 'after']) -> None:
+ """
+ Check that existing :py:mod:`sys.monitoring` code-local events are
+ preserved when a profiler swaps out the callable's code object.
+ """
+ prof = LineProfiler(wrap_trace=True)
+
+ @prof
+ def func0(n: int) -> int:
+ """
+ This function compiles down to the same bytecode as `func()` and
+ ensure that `prof` does bytecode padding with the latter later.
+ """
+ x = 0
+ for n in range(1, n + 1):
+ x += n
+ return x
+
+ def func(n: int) -> int:
+ x = 0
+ for n in range(1, n + 1):
+ x += n # Loop body
+ return x
+
+ def profile() -> None:
+ nonlocal code
+ nonlocal func
+ orig_code = func.__code__
+ orig_func, func = func, prof(func)
+ code = orig_func.__code__
+ assert code is not orig_code, (
+ '`line_profiler` didn\'t overwrite the function\'s code object')
+
+ lines, first_lineno = inspect.getsourcelines(func)
+ lineno_loop = first_lineno + next(
+ offset for offset, line in enumerate(lines)
+ if line.rstrip().endswith('# Loop body'))
+ names = {func.__name__, func.__qualname__}
+ code = func.__code__
+ callback = LineCallback(lambda code, _: code.co_name in names)
+
+ n = 17
+ try:
+ with ExitStack() as stack:
+ stack.enter_context(restore_events())
+ stack.enter_context(restore_events(code=code))
+ # Disable global line events, and enable local line events
+ disable_line_events()
+ if profile_when == 'before':
+ profile()
+ enable_line_events(code)
+ if profile_when != 'before':
+ # If we're here, the code object of `func()` is swapped
+ # out after code-local events have been registered to it
+ profile()
+ assert MON.get_current_callback() is callback
+ assert func(n) == n * (n + 1) // 2
+ assert MON.get_current_callback() is callback
+ print(callback.nhits)
+ assert callback.nhits[_line_profiler.label(code)][lineno_loop] == n
+ finally:
+ with StringIO() as sio:
+ prof.print_stats(sio)
+ output = sio.getvalue()
+ print(output)
+ line = next(line for line in output.splitlines()
+ if line.endswith('# Loop body'))
+ nhits = int(line.split()[1])
+ assert nhits == n