Skip to content

Commit 0c0b0ed

Browse files
isabelizimmwesm
authored andcommitted
Merged PR posit-dev/positron-python#114: Add ipython magics view
Merge pull request #114 from posit-dev/magic-viewer -------------------- Commit message for posit-dev/positron-python@6bab2da: add all requirements from positron_jedilsp -------------------- Commit message for posit-dev/positron-python@7f92125: add pygls to build requirements -------------------- Commit message for posit-dev/positron-python@38571ec: Merge branch 'main' into magic-viewer -------------------- Commit message for posit-dev/positron-python@c01c112: add build requirements -------------------- Commit message for posit-dev/positron-python@41122f6: linting -------------------- Commit message for posit-dev/positron-python@2ae5dd5: add beginning of tests These tests run an ipython kernel with a magic registered. They will check to see if the %view magic exists and not error on run for pandas dataframes and expressions. It will also check that a TypeError is thrown if an incorrect type is used. -------------------- Commit message for posit-dev/positron-python@934b395: changes from suggestions -------------------- Commit message for posit-dev/positron-python@194754a: Apply suggestions from code review Co-authored-by: Wasim Lorgat <[email protected]> -------------------- Commit message for posit-dev/positron-python@495b2b3: linting -------------------- Commit message for posit-dev/positron-python@067e02d: add ipython ViewerMagic Create a linemagic that ingests user input and opens up in DataViewer tab Authored-by: Isabel Zimmerman <[email protected]> Signed-off-by: Isabel Zimmerman <[email protected]>
1 parent d5b39c9 commit 0c0b0ed

File tree

4 files changed

+85
-2
lines changed

4 files changed

+85
-2
lines changed

extensions/positron-python/build/test-requirements.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ yapf
1111
pylint
1212
pycodestyle
1313
pydocstyle
14+
pygls
1415
prospector
1516
pytest
1617
flask
@@ -29,6 +30,11 @@ freezegun
2930
# --- Start Positron ---
3031
# iPython Kernel tests
3132
ipykernel
33+
jedi
34+
jedi_language_server
35+
lsprotocol
3236
matplotlib
37+
pandas
3338
pytest-mock
39+
polars
3440
# --- End Positron ---

extensions/positron-python/pythonFiles/positron/inspectors.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def copy(self, value: Any) -> Any:
118118
return copy.copy(value)
119119

120120
def to_dataset(self, value: Any, title: str) -> Optional[DataSet]:
121-
return None
121+
raise TypeError(f"Type {type(value)} is not supported by `View()`.")
122122

123123
def to_html(self, value: Any) -> str:
124124
return repr(value)

extensions/positron-python/pythonFiles/positron/positron_ipkernel.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
import logging
99
from collections.abc import Iterable, Sequence
1010
from itertools import chain
11-
from typing import Any, Optional, Tuple
11+
from typing import Any, Optional, Tuple, Dict
1212

1313
from ipykernel.ipkernel import IPythonKernel
1414
from ipykernel.zmqshell import ZMQInteractiveShell
1515

16+
from IPython.core.magic import Magics, magics_class, line_magic, needs_local_scope
17+
1618
from .dataviewer import DataViewerService
1719
from .environment import EnvironmentService
1820
from .inspectors import get_inspector
@@ -76,6 +78,7 @@ def __init__(self, **kwargs):
7678

7779
# Setup Positron's dataviewer service
7880
self.dataviewer_service = DataViewerService(POSITRON_DATA_VIEWER_COMM)
81+
load_ipython_extension(self.shell)
7982

8083
def do_shutdown(self, restart) -> dict:
8184
"""
@@ -358,3 +361,34 @@ async def _soft_reset(self, parent) -> dict:
358361
self.env_service.send_list()
359362

360363
return coro
364+
365+
366+
@magics_class
367+
class ViewerMagic(Magics):
368+
@needs_local_scope
369+
@line_magic
370+
def view(self, value: str, local_ns: Dict[str, Any]):
371+
"""Open DataViewerService through %view magic command"""
372+
373+
try:
374+
local_value = local_ns[value]
375+
inspector = get_inspector(local_value)
376+
dataset = inspector.to_dataset(local_value, value)
377+
except KeyError: # not in namespace
378+
eval_value = eval(value, local_ns, local_ns)
379+
inspector = get_inspector(eval_value)
380+
dataset = inspector.to_dataset(eval_value, value)
381+
382+
if dataset is not None:
383+
DataViewerService(POSITRON_DATA_VIEWER_COMM).register_dataset(dataset)
384+
385+
386+
def load_ipython_extension(ipython):
387+
"""
388+
Any module file that define a function named `load_ipython_extension`
389+
can be loaded via `%load_ext module.path` or be configured to be
390+
autoloaded by IPython at startup time.
391+
"""
392+
# You can register the class itself without instantiating it. IPython will
393+
# call the default constructor on it.
394+
ipython.register_magics(ViewerMagic)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import pytest
2+
3+
import pandas as pd
4+
import polars as pl
5+
from IPython.conftest import get_ipython
6+
7+
from positron.positron_ipkernel import ViewerMagic
8+
9+
10+
@pytest.fixture(scope="session")
11+
def get_ip():
12+
ip = get_ipython()
13+
ip.register_magics(ViewerMagic)
14+
return ip
15+
16+
17+
def test_pandas_df_expression(get_ip):
18+
get_ip.run_cell("import pandas as pd\n" "%view pd.DataFrame({'x': [1,2,3]})")
19+
20+
assert "view" in get_ip.magics_manager.magics["line"]
21+
22+
23+
def test_pandas_df_var(get_ip):
24+
get_ip.run_cell(
25+
"import pandas as pd\n" "a = pd.DataFrame({'x': [1,2,3]})\n" "%view a", store_history=True
26+
)
27+
28+
assert "view" in get_ip.magics_manager.magics["line"]
29+
assert "view" in get_ip.user_ns["In"][1]
30+
assert isinstance(get_ip.user_ns["a"], pd.DataFrame)
31+
32+
33+
def test_polars_df_var(get_ip):
34+
get_ip.run_cell("import polars as pl\n" "a = pl.DataFrame()\n" "%view a", store_history=True)
35+
36+
assert "view" in get_ip.magics_manager.magics["line"]
37+
assert "view" in get_ip.user_ns["In"][1]
38+
assert isinstance(get_ip.user_ns["a"], pl.DataFrame)
39+
40+
41+
def test_unsupported_type(get_ip):
42+
with pytest.raises(TypeError):
43+
get_ip.run_line_magic("view", "12")

0 commit comments

Comments
 (0)