Source code for Karana.KUtils._PythonReentryMonitor

# Copyright (c) 2024-2026 Karana Dynamics Pty Ltd. All rights reserved.
#
# NOTICE TO USER:
#
# This source code and/or documentation (the "Licensed Materials") is
# the confidential and proprietary information of Karana Dynamics Inc.
# Use of these Licensed Materials is governed by the terms and conditions
# of a separate software license agreement between Karana Dynamics and the
# Licensee ("License Agreement"). Unless expressly permitted under that
# agreement, any reproduction, modification, distribution, or disclosure
# of the Licensed Materials, in whole or in part, to any third party
# without the prior written consent of Karana Dynamics is strictly prohibited.
#
# THE LICENSED MATERIALS ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
# KARANA DYNAMICS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
# BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, AND
# FITNESS FOR A PARTICULAR PURPOSE.
#
# IN NO EVENT SHALL KARANA DYNAMICS BE LIABLE FOR ANY DAMAGES WHATSOEVER,
# INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS, DATA, OR USE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, WHETHER IN CONTRACT, TORT,
# OR OTHERWISE ARISING OUT OF OR IN CONNECTION WITH THE LICENSED MATERIALS.
#
# U.S. Government End Users: The Licensed Materials are a "commercial item"
# as defined at 48 C.F.R. 2.101, and are provided to the U.S. Government
# only as a commercial end item under the terms of this license.
#
# Any use of the Licensed Materials in individual or commercial software must
# include, in the user documentation and internal source code comments,
# this Notice, Disclaimer, and U.S. Government Use Provision.

import traceback
from sys import setprofile, getprofile


[docs] class PythonReentryMonitor: """Monitor when you re-enter Python from C++. This class is designed to help you identify places in your simulation where you are re-entering the Python interpreter from C++. This can help identify places in your simulation where you can improve performance by migrating to C++. """ def __init__(self): self._depth = 0 self.traces = []
[docs] def trace(self, frame, event, _): """Trace the current line of Python code. This will track any call event. This does not count recursive events, so it will only track call events made at the top-level. Parameters ---------- frame : The Python frame. event : The event being traced. _ : arg, unused. Returns ------- The trace method: """ if event == "call": if self._depth == 0: # Capture full stack trace at entry stack = traceback.format_stack(frame) self.traces.append(stack) self._depth += 1 elif event == "return": self._depth -= 1 return self.trace
[docs] def __enter__(self): """Use this as a context. This will start monitoring all calls to Python in the context. Returns ------- Self This object. """ # Set the dept to 1, because the return from this __enter__ method will decrease the depth by 1 self._depth = 1 self._old_profile = getprofile() setprofile(self.trace) return self
[docs] def __exit__(self, *_): """ Exit the context. See __enter__ for details. Parameters ---------- *_ Exit context parameters. Unused. Returns ------- bool True if this exited successfully. """ setprofile(self._old_profile) # Pop the last trace, because __exit__ will be the most recent trace self.traces.pop(-1) return True
[docs] def dump(self): """Dump the traces captured by this monitor.""" print(f"There were {len(self.traces)} calls back into Python. Stack traces follow.") for k, t in enumerate(self.traces): print(f"Trace {k}:") print("".join(t))
[docs] def clear(self): """Clear out all the traces this monitor has captured.""" self.traces.clear()