Skip to content

Commit

Permalink
reset profile
Browse files Browse the repository at this point in the history
  • Loading branch information
Quentin Peter committed Mar 30, 2023
1 parent bf6f444 commit ee83722
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 0 deletions.
30 changes: 30 additions & 0 deletions spyder_kernels/console/tests/test_console_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -1406,5 +1406,35 @@ def test_django_settings(kernel):
assert "'settings':" in nsview


@flaky(max_runs=3)
def test_running_namespace_profile(tmpdir):
"""
Test that profile can get variables from running namespace.
"""
# Command to start the kernel
cmd = "from spyder_kernels.console import start; start.main()"

with setup_kernel(cmd) as client:
# Remove all variables
client.execute_interactive("%reset -f", timeout=TIMEOUT)

# Write defined variable code to a file
code = "result = 10\n%profile print(result)\nsucess=True"
d = tmpdir.join("defined-test.ipy")
d.write(code)

# Run code file `d`
client.execute_interactive("%runfile {}"
.format(repr(str(d))), timeout=TIMEOUT)

# Verify that `result` is defined in the current namespace
client.inspect('sucess')
msg = client.get_shell_msg(timeout=TIMEOUT)
while "found" not in msg['content']:
msg = client.get_shell_msg(timeout=TIMEOUT)
content = msg['content']
assert content['found']


if __name__ == "__main__":
pytest.main()
88 changes: 88 additions & 0 deletions spyder_kernels/customize/code_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,29 @@ def debugfile(self, line, local_ns=None):
context_locals=local_ns,
)

@runfile_arguments
@needs_local_scope
@line_magic
def profilefile(self, line, local_ns=None):
"""
Profile a file.
"""
args, local_ns = self._parse_runfile_argstring(
self.profilefile, line, local_ns)

with self._profile_exec() as prof_exec:
self._exec_file(
filename=args.filename,
canonic_filename=args.canonic_filename,
wdir=args.wdir,
current_namespace=args.current_namespace,
args=args.args,
exec_fun=prof_exec,
post_mortem=args.post_mortem,
context_globals=args.namespace,
context_locals=local_ns,
)

@runcell_arguments
@needs_local_scope
@line_magic
Expand Down Expand Up @@ -235,6 +258,36 @@ def debugcell(self, line, local_ns=None):
context_locals=local_ns,
)

@runcell_arguments
@needs_local_scope
@line_magic
def profilecell(self, line, local_ns=None):
"""
Profile a code cell from an editor.
"""
args = self._parse_runcell_argstring(self.profilecell, line)

with self._profile_exec() as prof_exec:
return self._exec_cell(
cell_id=args.cell_id,
filename=args.filename,
canonic_filename=args.canonic_filename,
exec_fun=prof_exec,
post_mortem=args.post_mortem,
context_globals=self.shell.user_ns,
context_locals=local_ns,
)

@no_var_expand
@needs_local_scope
@line_cell_magic
def profile(self, line, cell=None, local_ns=None):
"""Profile the given line."""
if cell is not None:
line += "\n" + cell
with self._profile_exec() as prof_exec:
return prof_exec(line, self.shell.user_ns, local_ns)

@contextmanager
def _debugger_exec(self, filename, continue_if_has_breakpoints):
"""Get an exec function to use for debugging."""
Expand Down Expand Up @@ -274,6 +327,41 @@ def debug_exec(code, glob, loc):
# debugger (self) is not aware of this.
session._previous_step = None

@contextmanager
def _profile_exec(self):
"""Get an exec function for profiling."""
with tempfile.TemporaryDirectory() as tempdir:
# Reset the tracing function in case we are debugging
trace_fun = sys.gettrace()
sys.settrace(None)
# Get a file to save the results
profile_filename = os.path.join(tempdir, "profile.prof")
try:
if self.shell.is_debugging():
def prof_exec(code, glob, loc):
# if we are debugging (tracing), call_tracing is
# necessary for profiling
return sys.call_tracing(cProfile.runctx, (
code, glob, loc, profile_filename
))
yield prof_exec
else:
yield partial(cProfile.runctx, filename=profile_filename)
finally:
# Resect tracing function
sys.settrace(trace_fun)
if os.path.isfile(profile_filename):
# Send result to frontend
with open(profile_filename, "br") as f:
profile_result = f.read()
try:
frontend_request(blocking=False).show_profile_file(
profile_result, create_pathlist()
)
except CommError:
logger.debug(
"Could not send profile result to the frontend.")

def _exec_file(
self,
filename=None,
Expand Down

0 comments on commit ee83722

Please sign in to comment.