diff --git a/IPython/html/static/notebook/js/notebook.js b/IPython/html/static/notebook/js/notebook.js index 492793b..8181138 100644 --- a/IPython/html/static/notebook/js/notebook.js +++ b/IPython/html/static/notebook/js/notebook.js @@ -207,9 +207,18 @@ define([ }); this.events.on('spec_changed.Kernel', function(event, data) { - that.set_kernelspec_metadata(data); + that.metadata.kernelspec = + {name: data.name, display_name: data.display_name}; + }); + + this.events.on('kernel_ready.Kernel', function(event, data) { + var kinfo = data.kernel.info_reply + var langinfo = kinfo.language_info || {}; + if (!langinfo.name) langinfo.name = kinfo.language; + + that.metadata.language_info = langinfo; // Mode 'null' should be plain, unhighlighted text. - cm_mode = data.codemirror_mode || data.language || 'null' + var cm_mode = langinfo.codemirror_mode || langinfo.language || 'null' that.set_codemirror_mode(cm_mode); }); @@ -329,21 +338,11 @@ define([ md: this.metadata, callback: function (md) { that.metadata = md; - }, + }, name: 'Notebook', notebook: this, keyboard_manager: this.keyboard_manager}); }; - - Notebook.prototype.set_kernelspec_metadata = function(ks) { - var tostore = {}; - $.map(ks, function(value, field) { - if (field !== 'argv' && field !== 'env') { - tostore[field] = value; - } - }); - this.metadata.kernelspec = tostore; - } // Cell indexing, retrieval, etc. @@ -1811,6 +1810,14 @@ define([ this.events.trigger('spec_changed.Kernel', this.metadata.kernelspec); } + // Set the codemirror mode from language_info metadata + if (this.metadata.language_info !== undefined) { + var langinfo = this.metadata.language_info; + // Mode 'null' should be plain, unhighlighted text. + var cm_mode = langinfo.codemirror_mode || langinfo.language || 'null' + this.set_codemirror_mode(cm_mode); + } + // Only handle 1 worksheet for now. var worksheet = content.worksheets[0]; if (worksheet !== undefined) { diff --git a/IPython/html/static/services/kernels/kernel.js b/IPython/html/static/services/kernels/kernel.js index 9ad4fcb..38ac473 100644 --- a/IPython/html/static/services/kernels/kernel.js +++ b/IPython/html/static/services/kernels/kernel.js @@ -46,6 +46,7 @@ define([ this.username = "username"; this.session_id = utils.uuid(); this._msg_callbacks = {}; + this.info_reply = {}; // kernel_info_reply stored here after starting if (typeof(WebSocket) !== 'undefined') { this.WebSocket = WebSocket; @@ -398,7 +399,8 @@ define([ this.events.trigger('kernel_starting.Kernel', {kernel: this}); // get kernel info so we know what state the kernel is in var that = this; - this.kernel_info(function () { + this.kernel_info(function (reply) { + that.info_reply = reply.content; that.events.trigger('kernel_ready.Kernel', {kernel: that}); }); }; @@ -925,7 +927,8 @@ define([ } else if (execution_state === 'starting') { this.events.trigger('kernel_starting.Kernel', {kernel: this}); var that = this; - this.kernel_info(function () { + this.kernel_info(function (reply) { + that.info_reply = reply.content; that.events.trigger('kernel_ready.Kernel', {kernel: that}); }); diff --git a/IPython/kernel/kernelspec.py b/IPython/kernel/kernelspec.py index 63ea62a..58d3250 100644 --- a/IPython/kernel/kernelspec.py +++ b/IPython/kernel/kernelspec.py @@ -36,18 +36,9 @@ def _pythonfirst(s): class KernelSpec(HasTraits): argv = List() display_name = Unicode() - language = Unicode() - codemirror_mode = Any() # can be unicode or dict - pygments_lexer = Unicode() env = Dict() resource_dir = Unicode() - def _codemirror_mode_default(self): - return self.language - - def _pygments_lexer_default(self): - return self.language - @classmethod def from_resource_dir(cls, resource_dir): """Create a KernelSpec object by reading kernel.json @@ -63,12 +54,7 @@ class KernelSpec(HasTraits): d = dict(argv=self.argv, env=self.env, display_name=self.display_name, - language=self.language, ) - if self.codemirror_mode != self.language: - d['codemirror_mode'] = self.codemirror_mode - if self.pygments_lexer != self.language: - d['pygments_lexer'] = self.pygments_lexer return d @@ -118,12 +104,8 @@ class KernelSpecManager(HasTraits): process. This will put its informatino in the user kernels directory. """ return {'argv': make_ipkernel_cmd(), - 'display_name': 'IPython (Python %d)' % (3 if PY3 else 2), - 'language': 'python', - 'codemirror_mode': {'name': 'ipython', - 'version': sys.version_info[0]}, - 'pygments_lexer': 'ipython%d' % (3 if PY3 else 2), - } + 'display_name': 'IPython (Python %d)' % (3 if PY3 else 2), + } @property def _native_kernel_resource_dir(self): diff --git a/IPython/kernel/tests/test_kernelspec.py b/IPython/kernel/tests/test_kernelspec.py index 5275943..d909784 100644 --- a/IPython/kernel/tests/test_kernelspec.py +++ b/IPython/kernel/tests/test_kernelspec.py @@ -9,7 +9,6 @@ from IPython.kernel import kernelspec sample_kernel_json = {'argv':['cat', '{connection_file}'], 'display_name':'Test kernel', - 'language':'bash', } class KernelSpecTests(unittest.TestCase): @@ -39,8 +38,6 @@ class KernelSpecTests(unittest.TestCase): self.assertEqual(ks.resource_dir, self.sample_kernel_dir) self.assertEqual(ks.argv, sample_kernel_json['argv']) self.assertEqual(ks.display_name, sample_kernel_json['display_name']) - self.assertEqual(ks.language, sample_kernel_json['language']) - self.assertEqual(ks.codemirror_mode, sample_kernel_json['language']) self.assertEqual(ks.env, {}) def test_install_kernel_spec(self): diff --git a/IPython/kernel/tests/test_message_spec.py b/IPython/kernel/tests/test_message_spec.py index 3716a9f..3a72275 100644 --- a/IPython/kernel/tests/test_message_spec.py +++ b/IPython/kernel/tests/test_message_spec.py @@ -157,6 +157,7 @@ class KernelInfoReply(Reference): implementation_version = Version(min='2.1') language_version = Version(min='2.7') language = Unicode('python') + language_info = Dict() banner = Unicode() diff --git a/IPython/kernel/zmq/ipkernel.py b/IPython/kernel/zmq/ipkernel.py index 8c6e8c8..d64b521 100644 --- a/IPython/kernel/zmq/ipkernel.py +++ b/IPython/kernel/zmq/ipkernel.py @@ -71,6 +71,11 @@ class IPythonKernel(KernelBase): implementation_version = release.version language = 'python' language_version = sys.version.split()[0] + language_info = {'mimetype': 'text/x-python', + 'codemirror_mode': {'name': 'ipython', + 'version': sys.version_info[0]}, + 'pygments_lexer': 'ipython%d' % (3 if PY3 else 2), + } @property def banner(self): return self.shell.banner diff --git a/IPython/kernel/zmq/kernelbase.py b/IPython/kernel/zmq/kernelbase.py index 85f7beb..42b38c0 100755 --- a/IPython/kernel/zmq/kernelbase.py +++ b/IPython/kernel/zmq/kernelbase.py @@ -60,6 +60,10 @@ class Kernel(SingletonConfigurable): def _ident_default(self): return unicode_type(uuid.uuid4()) + # This should be overridden by wrapper kernels that implement any real + # language. + language_info = {} + # Private interface _darwin_app_nap = Bool(True, config=True, @@ -453,6 +457,7 @@ class Kernel(SingletonConfigurable): 'implementation_version': self.implementation_version, 'language': self.language, 'language_version': self.language_version, + 'language_info': self.language_info, 'banner': self.banner, } diff --git a/IPython/nbconvert/exporters/html.py b/IPython/nbconvert/exporters/html.py index 3bd4851..4af0967 100644 --- a/IPython/nbconvert/exporters/html.py +++ b/IPython/nbconvert/exporters/html.py @@ -59,8 +59,8 @@ class HTMLExporter(TemplateExporter): return c def from_notebook_node(self, nb, resources=None, **kw): - kernelspec = nb.metadata.get('kernelspec', {}) - lexer = kernelspec.get('pygments_lexer', kernelspec.get('language', None)) + langinfo = nb.metadata.get('language_info', {}) + lexer = langinfo.get('pygments_lexer', langinfo.get('name', None)) self.register_filter('highlight_code', Highlight2HTML(pygments_lexer=lexer, parent=self)) return super(HTMLExporter, self).from_notebook_node(nb, resources, **kw) diff --git a/IPython/nbconvert/exporters/latex.py b/IPython/nbconvert/exporters/latex.py index 94aad0e..8b387ac 100644 --- a/IPython/nbconvert/exporters/latex.py +++ b/IPython/nbconvert/exporters/latex.py @@ -89,8 +89,8 @@ class LatexExporter(TemplateExporter): return c def from_notebook_node(self, nb, resources=None, **kw): - kernelspec = nb.metadata.get('kernelspec', {}) - lexer = kernelspec.get('pygments_lexer', kernelspec.get('language', None)) + langinfo = nb.metadata.get('language_info', {}) + lexer = langinfo.get('pygments_lexer', langinfo.get('name', None)) self.register_filter('highlight_code', Highlight2Latex(pygments_lexer=lexer, parent=self)) return super(LatexExporter, self).from_notebook_node(nb, resources, **kw) diff --git a/IPython/nbconvert/filters/highlight.py b/IPython/nbconvert/filters/highlight.py index 9b0b6e6..59c0df1 100644 --- a/IPython/nbconvert/filters/highlight.py +++ b/IPython/nbconvert/filters/highlight.py @@ -27,7 +27,7 @@ class Highlight2HTML(NbConvertBase): def _default_language_changed(self, name, old, new): warn('Setting default_language in config is deprecated, ' - 'please use kernelspecs instead.') + 'please use language_info metadata instead.') self.pygments_lexer = new def __call__(self, source, language=None, metadata=None): @@ -61,7 +61,7 @@ class Highlight2Latex(NbConvertBase): def _default_language_changed(self, name, old, new): warn('Setting default_language in config is deprecated, ' - 'please use kernelspecs instead.') + 'please use language_info metadata instead.') self.pygments_lexer = new def __call__(self, source, language=None, metadata=None, strip_verbatim=False): diff --git a/IPython/nbconvert/utils/base.py b/IPython/nbconvert/utils/base.py index 68b3880..c336bd2 100644 --- a/IPython/nbconvert/utils/base.py +++ b/IPython/nbconvert/utils/base.py @@ -35,7 +35,7 @@ class NbConvertBase(LoggingConfigurable): ) default_language = Unicode('ipython', config=True, - help='DEPRECATED default highlight language, please use kernelspecs instead') + help='DEPRECATED default highlight language, please use language_info metadata instead') def __init__(self, **kw): super(NbConvertBase, self).__init__(**kw) diff --git a/docs/source/development/kernels.rst b/docs/source/development/kernels.rst index 5c8fd0f..6336dc1 100644 --- a/docs/source/development/kernels.rst +++ b/docs/source/development/kernels.rst @@ -112,34 +112,16 @@ JSON serialised dictionary containing the following keys and values: - **display_name**: The kernel's name as it should be displayed in the UI. Unlike the kernel name used in the API, this can contain arbitrary unicode characters. -- **language**: The programming language which this kernel runs. This will be - stored in notebook metadata. This may be used by syntax highlighters to guess - how to parse code in a notebook, and frontends may eventually use it to - identify alternative kernels that can run some code. -- **codemirror_mode** (optional): The `codemirror mode `_ - to use for code in this language. This can be a string or a dictionary, as - passed to codemirror config. This only needs to be specified if it does not - match the value in *language*. -- **pygments_lexer** (optional): The name of a `Pygments lexer `_ - to use for code in this language, as a string. This only needs to be specified - if it does not match the value in *language*. - **env** (optional): A dictionary of environment variables to set for the kernel. These will be added to the current environment variables before the kernel is started. -- **help_links** (optional): A list of dictionaries, each with keys 'text' and - 'url'. These will be displayed in the help menu in the notebook UI. For example, the kernel.json file for IPython looks like this:: { "argv": ["python3", "-c", "from IPython.kernel.zmq.kernelapp import main; main()", - "-f", "{connection_file}"], - "codemirror_mode": { - "version": 3, - "name": "ipython" - }, + "-f", "{connection_file}"], "display_name": "IPython (Python 3)", - "language": "python" } To see the available kernel specs, run:: diff --git a/docs/source/development/messaging.rst b/docs/source/development/messaging.rst index f2a3be1..813fd8b 100644 --- a/docs/source/development/messaging.rst +++ b/docs/source/development/messaging.rst @@ -685,11 +685,33 @@ Message type: ``kernel_info_reply``:: # included in IPython. 'language_version': 'X.Y.Z', + # Information about the language of code for the kernel + 'language_info': { + 'mimetype': str, + + # Pygments lexer, for highlighting + # Only needed if it differs from the top level 'language' field. + 'pygments_lexer': str, + + # Codemirror mode, for for highlighting in the notebook. + # Only needed if it differs from the top level 'language' field. + 'codemirror_mode': str or dict, + }, + # A banner of information about the kernel, # which may be desplayed in console environments. 'banner' : str, + + # Optional: A list of dictionaries, each with keys 'text' and 'url'. + # These will be displayed in the help menu in the notebook UI. + 'help_links': [ + {'text': str, 'url': str} + ], } +Refer to the lists of available `Pygments lexers `_ +and `codemirror modes `_ for those fields. + .. versionchanged:: 5.0 Versions changed from lists of integers to strings. @@ -700,7 +722,8 @@ Message type: ``kernel_info_reply``:: .. versionchanged:: 5.0 - ``implementation``, ``implementation_version``, and ``banner`` keys are added. + ``language_info``, ``implementation``, ``implementation_version``, ``banner`` + and ``help_links`` keys are added. .. _msging_shutdown: diff --git a/docs/source/development/wrapperkernels.rst b/docs/source/development/wrapperkernels.rst index 73fc405..271c5da 100644 --- a/docs/source/development/wrapperkernels.rst +++ b/docs/source/development/wrapperkernels.rst @@ -34,6 +34,16 @@ following methods and attributes: interprets (e.g. Python). The 'banner' is displayed to the user in console UIs before the first prompt. All of these values are strings. + .. attribute:: language_info + + Language information for :ref:`msging_kernel_info` replies, in a dictionary. + This should contain the key ``mimetype`` with the mimetype of code in the + target language (e.g. ``'text/x-python'``). It may also contain keys + ``codemirror_mode`` and ``pygments_lexer`` if they need to differ from + :attr:`language`. + + Other keys may be added to this later. + .. method:: do_execute(code, silent, store_history=True, user_expressions=None, allow_stdin=False) Execute user code. @@ -71,6 +81,7 @@ Example implementation_version = '1.0' language = 'no-op' language_version = '0.1' + language_info = {'mimetype': 'text/plain'} banner = "Echo kernel - as useful as a parrot" def do_execute(self, code, silent, store_history=True, user_expressions=None, @@ -94,7 +105,6 @@ Here's the Kernel spec ``kernel.json`` file for this:: {"argv":["python","-m","echokernel", "-f", "{connection_file}"], "display_name":"Echo", - "language":"no-op" }