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

Test with jupyter_kernel_test #238

Merged
merged 3 commits into from
Dec 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,22 @@ test: clean
ipcluster start -n=3 &
pytest
ipcluster stop
python metakernel_python/test_metakernel_python.py
make clean

test_warn: clean
ipcluster start -n=3 &
export PYTHONWARNINGS="all"
pytest
ipcluster stop
python metakernel_python/test_metakernel_python.py
make clean

cover: clean
ipcluster start -n=3 &
pytest --cov=$(NAME)
ipcluster stop
python metakernel_python/test_metakernel_python.py
coverage annotate

release:
Expand Down
13 changes: 7 additions & 6 deletions metakernel/_metakernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ def do_history(self, hist_access_type, output, raw, session=None,
"""
with open(self.hist_file) as fid:
self.hist_cache = json.loads(fid.read() or "[]")
return {'history': [(None, None, h) for h in self.hist_cache]}
return {'status': 'ok', 'history': [(None, None, h) for h in self.hist_cache]}

def do_shutdown(self, restart):
"""
Expand Down Expand Up @@ -521,7 +521,8 @@ def do_complete(self, code, cursor_pos):
'matches': [],
'cursor_start': info['start'],
'cursor_end': info['end'],
'status': 'ok'
'status': 'ok',
'metadata': {}
}

matches = info['path_matches']
Expand Down Expand Up @@ -573,15 +574,15 @@ def do_complete(self, code, cursor_pos):

return content

def do_inspect(self, code, cursor_pos, detail_level=0):
def do_inspect(self, code, cursor_pos, detail_level=0, omit_sections=()):
"""Object introspection.

https://jupyter-client.readthedocs.io/en/stable/messaging.html#introspection
"""
if cursor_pos > len(code):
return

content = {'status': 'aborted', 'data': {}, 'found': False}
content = {'status': 'aborted', 'data': {}, 'found': False, 'metadata': {}}
docstring = self.get_help_on(code, detail_level, none_on_fail=True,
cursor_pos=cursor_pos)

Expand Down Expand Up @@ -695,7 +696,7 @@ def Error(self, *objects, **kwargs):
self.log.info(message.rstrip())
else:
self.send_response(self.iopub_socket, 'stream', stream_content)

def Error_display(self, *objects, **kwargs):
"""Print `objects` to stdout is they area strings, separated by `sep` and followed by `end`.
All other objects are rendered using the Display method
Expand All @@ -710,7 +711,7 @@ def Error_display(self, *objects, **kwargs):
else:
# msg is the error for str
msg.append(item)

for k,v in kwargs:
if not isinstance(v, str):
self.Display(k,v)
Expand Down
1 change: 0 additions & 1 deletion metakernel/magics/python_magic.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,5 @@ def get_help_on(self, info, level=0, none_on_fail=False):
else:
return strhelp


def register_magics(kernel):
kernel.register_magics(PythonMagic)
11 changes: 11 additions & 0 deletions metakernel_python/metakernel_python.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import print_function

from IPython.core.inputtransformer2 import TransformerManager
from metakernel import MetaKernel
import sys

Expand Down Expand Up @@ -30,6 +31,10 @@ class MetaKernelPython(MetaKernel):
"name": "metakernel_python"
}

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.transformer_manager = TransformerManager()

def get_usage(self):
return ("This is MetaKernel Python. It implements a Python " +
"interpreter.")
Expand Down Expand Up @@ -60,6 +65,12 @@ def get_kernel_help_on(self, info, level=0, none_on_fail=False):
python_magic = self.line_magics['python']
return python_magic.get_help_on(info, level, none_on_fail)

def do_is_complete(self, code):
status, indent_spaces = self.transformer_manager.check_complete(code)
r = {'status': status}
if status == 'incomplete':
r['indent'] = ' ' * indent_spaces
return r

if __name__ == '__main__':
MetaKernelPython.run_as_main()
90 changes: 90 additions & 0 deletions metakernel_python/test_metakernel_python.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
"""
Test the metakernel_python kernel using jupyter_kernel_test with supported capabilities
"""
import unittest
import jupyter_kernel_test as jkt

class MetaKernelPythonTests(jkt.KernelTests):

# REQUIRED

# the kernel to be tested
# this is the normally the name of the directory containing the
# kernel.json file - you should be able to do
# `jupyter console --kernel KERNEL_NAME`
kernel_name = "metakernel_python"

# Everything else is OPTIONAL

# the name of the language the kernel executes
# checked against language_info.name in kernel_info_reply
language_name = "python"

# the normal file extension (including the leading dot) for this language
# checked against language_info.file_extension in kernel_info_reply
file_extension = ".py"

# code which should write the exact string `hello, world` to STDOUT
code_hello_world = "print('hello, world')"

# code which should cause (any) text to be written to STDERR
code_stderr = "import sys; print('test', file=sys.stderr)"

# samples for the autocompletion functionality
# for each dictionary, `text` is the input to try and complete, and
# `matches` the list of all complete matching strings which should be found
completion_samples = [
{
'text': 'zi',
'matches': {'zip'},
},
]

# samples for testing code-completeness (used by console only)
# these samples should respectively be unambigiously complete statements
# (which should be executed on <enter>), incomplete statements or code
# which should be identified as invalid
complete_code_samples = ['1', "print('hello, world')", "def f(x):\n return x*2\n\n\n"]
incomplete_code_samples = ["print('''hello", "def f(x):\n x*2"]
invalid_code_samples = ['import = 7q']

# code which should cause a help pager to be displayed (as of 4.1, this is
# displayed by the notebook only as inline text, so it's probably more
# useful for console clients)
code_page_something = "zip?"

# code which should generate a (user-level) error in the kernel, and send
# a traceback to the client
code_generate_error = "raise"

# a statement or block of code which generates a result (which is shown
# as Out[n] instead of just something printed to stdout)
# running each `code` should cause `result` to be displayed (note that the
# result here will always be a string representation of whatever the actual
# result type is - be careful of string formatting)
code_execute_result = [
{'code': "1+2+3", 'result': "6"},
{'code': "[n*n for n in range(1, 4)]", 'result': "[1, 4, 9]"}
]

# code which generates some sort of rich output
# for each `code` input a single rich display object with the specified
# `mime` type should be sent to the frontend
# note that this expects a `display_data` message rather than
# `execute_result`; this test might be a little too inflexible in some cases
code_display_data = [
{'code': "from IPython.display import HTML, display; display(HTML('<b>test</b>'))",
'mime': "text/html"},
{'code': "from IPython.display import Math, display; display(Math('\\frac{1}{2}'))",
'mime': "text/latex"}
]

# test the support for object inspection
# the sample should be a name about which the kernel can give some help
# information (a built-in function is probably a good choice)
# only the default inspection level (equivalent to ipython "obj?")
# is currently tested
code_inspect_sample = "zip"

if __name__ == '__main__':
unittest.main()
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ install_requires =
pexpect >=4.2
jedi<0.18; python_version<="3.6"
[options.extras_require]
test = pytest;pytest-cov;requests
test = pytest;pytest-cov;requests;jupyter_kernel_test
activity = portalocker # activity magic
parallel = ipyparallel # parallel magic

Expand Down