Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Profile in the IPython console to gain access to variables and get a result in case of cancellation #15372

Open
wants to merge 69 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
50187eb
add profiler plugin
Mar 30, 2023
467aa9d
git subrepo clone --branch=improve_namespace --force https://github.c…
Mar 30, 2023
7b2f1e9
Merge branch 'magic_runfile' into profile_script
Mar 31, 2023
e9c32f3
Merge branch 'magic_runfile' into profile_script
Apr 1, 2023
0b30600
Merge branch 'magic_runfile' into profile_script
Apr 1, 2023
ee0cf11
Merge remote-tracking branch 'upstream/master' into profile_script
Apr 2, 2023
d9b674f
git subrepo clone --branch=improve_namespace --force https://github.c…
Apr 2, 2023
088091c
Merge remote-tracking branch 'upstream/master' into profile_script
Apr 5, 2023
bc88694
Merge remote-tracking branch 'upstream/master' into profile_script
Jun 19, 2023
ee208b7
git subrepo clone (merge) --branch=improve_namespace --force https://…
Jun 19, 2023
ef4e208
Add profile to run menu
Jun 19, 2023
36fb181
refresh
Jun 19, 2023
9c1ed93
remove self. before actions
Jun 20, 2023
d485278
fix test
Jun 21, 2023
e035e31
Merge remote-tracking branch 'upstream/master' into profile_script
Jun 21, 2023
cf7aa50
Merge branch 'master' into profile_script
impact27 Jun 21, 2023
3c5d200
fix manifest
Jun 21, 2023
fdc1d57
fix import
Jun 21, 2023
5f80638
Merge remote-tracking branch 'upstream/master' into profile_script
Jun 22, 2023
c75af79
Merge remote-tracking branch 'upstream/master' into profile_script
Jun 25, 2023
ce40f8c
profile ipy files
Jun 25, 2023
f5c48e4
Merge remote-tracking branch 'upstream/master' into profile_script
Aug 1, 2023
5e608a1
git subrepo clone (merge) --branch=profile_script --force https://git…
Aug 1, 2023
5d8dda2
Apply suggestions from code review
impact27 Aug 5, 2023
6aaa12e
Update spyder/plugins/profiler/widgets/profiler_data_tree.py
impact27 Aug 5, 2023
89f9538
git subrepo clone (merge) --branch=improve_namespace --force https://…
Aug 5, 2023
83883b0
git subrepo clone (merge) --branch=improve_namespace --force https://…
Aug 5, 2023
3fbac2b
Merge remote-tracking branch 'upstream/master' into profile_script
Aug 5, 2023
ab3b70c
temps folder on linux
Aug 5, 2023
34e4a51
Merge remote-tracking branch 'upstream/master' into profile_script
Nov 17, 2023
1befd67
pep8
Nov 17, 2023
c9d661e
Merge branch 'master' into profile_script
impact27 Nov 18, 2023
d480379
Merge branch 'master' into profile_script
Mar 12, 2024
e6aece7
git subrepo clone (merge) --branch=improve_namespace --force https://…
Mar 12, 2024
bf4d6fa
Merge branch 'master' into profile_script
Mar 14, 2024
42c5622
git subrepo clone (merge) --branch=improve_namespace --force https://…
Mar 14, 2024
6e66329
Merge remote-tracking branch 'upstream/master' into profile_script
Mar 17, 2024
d3d79ac
git subrepo clone --branch=improve_namespace --force https://github.c…
Mar 17, 2024
8532ccd
Merge remote-tracking branch 'upstream/master' into profile_script
May 23, 2024
e22b6f9
git subrepo clone (merge) --branch=improve_namespace --force https://…
May 23, 2024
e41455f
fix test
May 29, 2024
413f619
Merge branch 'master' into profile_script
May 30, 2024
2b8343a
git subrepo clone (merge) --branch=improve_namespace --force https://…
May 30, 2024
097ccc2
merge master
Sep 3, 2024
381fe23
git subrepo clone (merge) --branch=improve_namespace --force https://…
Sep 3, 2024
bd87d1f
fix import
Sep 3, 2024
c9d0eab
Backport PR #22459: PR: Update workflows to run in the `6.x` branch (CI)
ccordoba12 Sep 7, 2024
1a2bd38
Merge pull request #22460 from meeseeksmachine/auto-backport-of-pr-22…
ccordoba12 Sep 7, 2024
61e6fdd
Backport PR #22437 on branch 6.x (PR: Fix bug when calling `update_ed…
meeseeksmachine Sep 7, 2024
334ce4c
Backport PR #22438 on branch 6.x (PR: Enable `autoreload` magic on al…
meeseeksmachine Sep 7, 2024
a44926b
Fix development version
ccordoba12 Sep 7, 2024
2c8b28f
Merge pull request #22468 from ccordoba12/fix-stable-version
ccordoba12 Sep 7, 2024
7913ca8
Backport PR #22474 on branch 6.x (PR: Update org.spyder_ide.spyder.ap…
meeseeksmachine Sep 9, 2024
b1f96ae
Backport PR #22476 on branch 6.x (PR: Fix issues showing the in-app a…
meeseeksmachine Sep 10, 2024
1b86cb7
Backport PR #22484 on branch 6.x (PR: Change default format in array …
meeseeksmachine Sep 11, 2024
24bdc68
Backport PR #22502 on branch 6.x (PR: Reset `undocked before hiding` …
meeseeksmachine Sep 14, 2024
b2843d1
Backport PR #22504 on branch 6.x (PR: Don't use script to get env var…
meeseeksmachine Sep 15, 2024
7284d89
Backport PR #22509 on branch 6.x (PR: Don't kill kernel process tree …
meeseeksmachine Sep 16, 2024
27747d1
Backport PR #22442: PR: Update contributing, release and maintenance …
dalthviz Sep 16, 2024
c24adcb
Merge pull request #22517 from meeseeksmachine/auto-backport-of-pr-22…
dalthviz Sep 16, 2024
e307e25
Backport PR #22424: PR: Prioritize conda-forge channel so that stable…
dalthviz Sep 17, 2024
456adad
Merge pull request #22518 from meeseeksmachine/auto-backport-of-pr-22…
dalthviz Sep 17, 2024
949e11b
Backport PR #22490: PR: Prevent error when updating `sys.path` in con…
dalthviz Sep 17, 2024
ada993e
Merge pull request #22523 from meeseeksmachine/auto-backport-of-pr-22…
dalthviz Sep 18, 2024
9cac28a
Merge remote-tracking branch 'upstream/6.x' into profile_script
Sep 18, 2024
3fccd35
git subrepo clone (merge) --branch=improve_namespace --force https://…
Sep 18, 2024
9ea4e2b
Merge remote-tracking branch 'upstream/master' into profile_script
Sep 23, 2024
2f1cf2d
fix empty pane widget
Oct 4, 2024
bd1302d
Merge remote-tracking branch 'upstream/master' into profile_script
Oct 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions external-deps/spyder-kernels/.gitrepo

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

168 changes: 168 additions & 0 deletions spyder/app/tests/test_mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
from spyder.plugins.ipythonconsole.api import IPythonConsolePyConfiguration
from spyder.plugins.mainmenu.api import ApplicationMenus
from spyder.plugins.layout.layouts import DefaultLayouts
from spyder.plugins.profiler.widgets.main_widget import ProfilerWidgetActions
from spyder.plugins.toolbar.api import ApplicationToolbars
from spyder.plugins.run.api import (
RunExecutionParameters, ExtendedRunExecutionParameters, WorkingDirOpts,
Expand Down Expand Up @@ -5256,6 +5257,173 @@ def test_add_external_plugins_to_dependencies(main_window, qtbot):
assert 'spyder-boilerplate' in external_names


def test_profiler(main_window, qtbot, tmpdir):
"""Test if profiler works."""
ipyconsole = main_window.ipyconsole
shell = ipyconsole.get_current_shellwidget()
qtbot.waitUntil(lambda: shell._prompt_html is not None,
timeout=SHELL_TIMEOUT)
control = ipyconsole.get_widget().get_focus_widget()
profiler = main_window.profiler
profile_tree = profiler.get_widget()

sleep_str = '<built-in method time.sleep>'

# Test simple profile
with qtbot.waitSignal(shell.executed):
shell.execute("import time")

assert len(profile_tree.current_widget().data_tree.get_items(2)) == 0

with qtbot.waitSignal(shell.executed):
shell.execute("%profile time.sleep(0.1)")
qtbot.wait(1000)

assert len(profile_tree.current_widget().data_tree.get_items(2)) == 1
item = profile_tree.current_widget().data_tree.get_items(2)[0].item_key[2]
assert item == sleep_str

toggle_tree_action = profile_tree.get_action(
ProfilerWidgetActions.ToggleTreeDirection)

# Make sure the ordering methods don't reveal the root element.
toggle_tree_action.setChecked(True)
assert len(profile_tree.current_widget().data_tree.get_items(0)) == 1
item = profile_tree.current_widget().data_tree.get_items(2)[0].item_key[2]
assert item == sleep_str
toggle_tree_action.setChecked(False)
assert len(profile_tree.current_widget().data_tree.get_items(2)) == 1
item = profile_tree.current_widget().data_tree.get_items(2)[0].item_key[2]
assert item == sleep_str
profile_tree.slow_local_tree()
assert len(profile_tree.current_widget().data_tree.get_items(0)) == 3

# Test profilecell
# Write code with a cell to a file
code = "result = 10; fname = __file__; time.sleep(0); time.time()"
p = tmpdir.join("cell-test.py")
p.write(code)
main_window.editor.load(to_text_string(p))

# Execute profile cell
with qtbot.waitSignal(shell.executed):
shell.execute("%profilecell -i 0 " + repr(to_text_string(p)))

qtbot.wait(1000)

# Verify that the `result` variable is defined
assert shell.get_value('result') == 10

# Verify that the `fname` variable is `cell-test.py`
assert "cell-test.py" in shell.get_value('fname')

# Verify that two elements are in the profiler
# Actually 3 because globals is included too
assert len(profile_tree.current_widget().data_tree.get_items(2)) == 3

# Test profilefile
code = (
"import time\n"
"def f():\n"
" g()\n"
" time.sleep(1)\n"
"def g(stop=False):\n"
" time.sleep(1)\n"
" if not stop:\n"
" g(True)\n"
"f()"
)
p = tmpdir.join("cell-test_2.py")
p.write(code)
main_window.editor.load(to_text_string(p))

with qtbot.waitSignal(shell.executed):
shell.execute("%profilefile " + repr(to_text_string(p)))
qtbot.wait(1000)
# Check callee tree
toggle_tree_action.setChecked(False)
assert len(profile_tree.current_widget().data_tree.get_items(1)) == 3
values = ["f", sleep_str, "g"]
for item, val in zip(
profile_tree.current_widget().data_tree.get_items(1), values):
assert val == item.item_key[2]

# Check caller tree
toggle_tree_action.setChecked(True)
assert len(profile_tree.current_widget().data_tree.get_items(1)) == 3
values = [sleep_str, "f", "g"]
for item, val in zip(
profile_tree.current_widget().data_tree.get_items(1), values):
assert val == item.item_key[2]

# Check local time
profile_tree.slow_local_tree()
assert len(profile_tree.current_widget().data_tree.get_items(1)) == 11

# Check no errors happened
assert "error" not in control.toPlainText().lower()

# Test profiling while debugging
# Reset the tree
with qtbot.waitSignal(shell.executed):
shell.execute("%profile 0")
assert len(profile_tree.current_widget().data_tree.get_items(1)) == 0

with qtbot.waitSignal(shell.executed):
shell.execute("%debug 0")

assert shell.is_debugging()
with qtbot.waitSignal(shell.executed):
shell.execute("%profilefile " + repr(to_text_string(p)))
qtbot.wait(1000)

assert len(profile_tree.current_widget().data_tree.get_items(1)) == 3
assert shell.is_debugging()
# Make sure the shell is not broken
with qtbot.waitSignal(shell.executed):
shell.execute("13 + 1234")
assert shell.is_debugging()
assert "1247" in shell._control.toPlainText()
with qtbot.waitSignal(shell.executed):
shell.execute("q")
assert not shell.is_debugging()


def test_profiler_namespace(main_window, qtbot, tmpdir):
"""Test that the profile magic finds the right namespace"""
ipyconsole = main_window.ipyconsole
shell = ipyconsole.get_current_shellwidget()
qtbot.waitUntil(lambda: shell._prompt_html is not None,
timeout=SHELL_TIMEOUT)
# Test profilefile
code = (
"result = 10\n"
"%profile print(result)"
)
p = tmpdir.join("test_prof.ipy")
p.write(code)
main_window.editor.load(to_text_string(p))

with qtbot.waitSignal(shell.executed):
shell.execute("%debug 0")
with qtbot.waitSignal(shell.executed):
shell.execute("%runfile " + repr(to_text_string(p)))
# Make sure no errors are shown
assert "error" not in shell._control.toPlainText().lower()

with qtbot.waitSignal(shell.executed):
shell.execute("q")

# Test in main namespace
with qtbot.waitSignal(shell.executed):
shell.execute("%reset -f")

with qtbot.waitSignal(shell.executed):
shell.execute("%runfile " + repr(to_text_string(p)))
# Make sure no errors are shown
assert "error" not in shell._control.toPlainText().lower()


def test_locals_globals_var_debug(main_window, qtbot, tmpdir):
"""Test that the debugger can handle variables named globals and locals."""
ipyconsole = main_window.ipyconsole
Expand Down
5 changes: 5 additions & 0 deletions spyder/config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,8 @@
('profiler',
{
'enable': True,
'switch_to_plugin': True,
'n_slow_children': 25
}),
('pylint',
{
Expand Down Expand Up @@ -491,6 +493,9 @@
'pylint/run file in pylint': "F8",
# -- Profiler --
'profiler/run file in profiler': "F10",
'profiler/run cell in profiler': "Alt+F10",
'profiler/run selection in profiler': "",
'profiler/find_action': "Ctrl+F",
# -- Switcher --
'switcher/file switcher': 'Ctrl+P',
'switcher/symbol finder': 'Ctrl+Alt+P',
Expand Down
37 changes: 37 additions & 0 deletions spyder/images/dark/profile_cell.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions spyder/images/dark/profile_selection.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions spyder/images/light/profile_cell.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions spyder/images/light/profile_selection.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 4 additions & 2 deletions spyder/plugins/ipythonconsole/widgets/debugging.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from IPython.lib.lexers import (
IPython3Lexer, Python3Lexer, bygroups, using
)
from pygments.token import Keyword, Operator
from pygments.token import Keyword, Operator, Text
from pygments.util import ClassNotFound
from qtconsole.rich_jupyter_widget import RichJupyterWidget
from qtpy.QtCore import QEvent
Expand All @@ -37,7 +37,9 @@ class SpyderIPy3Lexer(IPython3Lexer):
spyder_tokens = [
(r'(!)(\w+)(.*\n)', bygroups(Operator, Keyword, using(Python3Lexer))),
(r'(%)(\w+)(.*\n)', bygroups(Operator, Keyword, using(Python3Lexer))),
]
(r'(?s)(\s*)(%%profile)([^\n]*\n)(.*)', bygroups(
Text, Operator, Text, using(Python3Lexer))),
]
tokens['root'] = spyder_tokens + tokens['root']


Expand Down
1 change: 1 addition & 0 deletions spyder/plugins/mainmenu/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class RunMenuSections:
Run = 'run_section'
RunExtras = 'run_extras_section'
RunInExecutors = 'executors_section'
Profile = 'profile_section'

class DebugMenuSections:
StartDebug = 'start_debug_section'
Expand Down
Loading
Loading