|
KernelExecOnCells library and nbextensions |
|
========================================== |
|
|
|
The KernelExecOnCells library is a shared library for creating Jupyter |
|
nbextensions which transform code cell text using calls to the active kernel. |
|
|
|
This scheme has been applied to create several nbextensions which are also |
|
included in the repository. |
|
For instance, to prettify code, see the [code-prettify] nbextension, or to |
|
refactor python 2 code for python 3, see the [2to3] extension. |
|
These nbextensions are defined as simple plugins of the main KernelExecOnCells |
|
library. Defining such a plugin, [jupyter-autopep8], is described in the last section below. |
|
|
|
|
|
Compatible Kernels |
|
------------------ |
|
|
|
The library is kernel-language agnostic, as described in the [internals] |
|
section below. Essentially any kernel capable of interpreting and creating |
|
json-formatted strings, and sending them to the stream output (where print |
|
statements in most languages go) should be easy to integrate. |
|
Hopefully, that covers pretty much all languages! |
|
|
|
|
|
Options |
|
------- |
|
|
|
The library uses a series of options, describing the configuration of the |
|
plugin. Default values for these options are specified as an object in the |
|
plugin source file, and can be overriden by values loaded from config. |
|
There are a few nbextension-wide options, configurable using the |
|
[jupyter_nbextensions_configurator] or by editing the `notebook` section config |
|
file directly. |
|
If `mod_name` is the name of the plugin module (e.g. `code_prettify`, `2to3`, |
|
...) and `LANG` the lowercased kernel language (eg julia, python, r ...), then |
|
the options are as follows: |
|
|
|
- `mod_name.add_toolbar_button`: |
|
Whether to add a toolbar button to transform the selected cell(s). |
|
Defaults to `true`. |
|
|
|
- `mod_name.button_icon`: |
|
A font-awesome class defining the icon used for the toolbar button and |
|
actions. See [fontawesome] for available icon classes. |
|
Defaults to `fa-legal`. |
|
|
|
- `mod_name.button_label`: |
|
Toolbar button label text. Also used in the actions' help text. |
|
Defaults to `mod_name`. |
|
|
|
- `mod_name.register_hotkey`: |
|
Whether to register hotkeys to transform the selected cell(s)/whole notebook. |
|
Defaults to `true`. |
|
|
|
- `mod_name.hotkeys.process_all`: |
|
Hotkey to use to transform all the code cells in the notebook. |
|
Defaults to `Ctrl-Shift-L`. |
|
|
|
- `mod_name.hotkeys.process_selected`: |
|
Hotkey to use to transform the selected cell(s). |
|
Defaults to `Ctrl-L`. |
|
|
|
- `mod_name.show_alerts_for_errors`: |
|
Whether to show alerts for errors in the kernel calls. |
|
Defaults to `true`. |
|
|
|
- `mod_name.kernel_config_map_json`: |
|
The value of this key is a string which can be parsed into a json object |
|
giving the config for each kernel language. |
|
|
|
The following give the per-kernel options of the parsed json, using the |
|
language key `LANG`, to be replaced as appropriate: |
|
|
|
* `mod_name.kernel_config_map_json.LANG.library`: |
|
String to execute in the kernel in order to load any necessary kernel |
|
libraries. |
|
|
|
* `mod_name.kernel_config_map_json.LANG.replacements_json_to_kernel`: |
|
a list of pairs of strings, used as arguments to javascript's |
|
`String.replace(from, to)` to translate from a json string into a valid |
|
representation of the same string in the kernel language. Since json |
|
strings are particularly simple, this can often (as with the python |
|
language) be left as the default, an empty list. |
|
|
|
* `mod_name.kernel_config_map_json.LANG.prefix` and |
|
`mod_name.kernel_config_map_json.LANG.postfix`: |
|
strings added as bookends to the kernel string (translated from the json |
|
string using the replacements above) to make up the kernel prettifier call |
|
kernel's prettifier libraries. |
|
|
|
* `mod_name.kernel_config_map_json.LANG.trim_formatted_text`: |
|
Whether to trim whitespace from the transformed cell text. Since jupyter |
|
cells don't usually have leading or trailing whitespace, the default |
|
behaviour is to trim the transformed text, in order to prevent the |
|
transform adding extra newlines at the end (a common behaviour for source |
|
files, where having a trailing newline is often considered good practice). |
|
|
|
|
|
Internals |
|
--------- |
|
|
|
The model is essentially: |
|
|
|
1. The cell text is grabbed by client-side javascript, then turned into a json |
|
string using javascript `JSON.stringify`. Since json-compatible strings are |
|
a particularly simple string format, which is compatible with many other |
|
programming languages without much modification (e.g. a valid json string |
|
is also a valid string in python 3, and also in python 2 when prefixed with |
|
a `u`), and easily converted for use in others (because of its simplicity). |
|
|
|
2. Optional regex replacements are used to translate the json-format string |
|
into a valid kernel string. Python, R and javascript don't require this |
|
step, but other languages may do, so it's implemented for flexibility |
|
using the per-kernel config key `replacements_json_to_kernel`, which is a |
|
list of pairs of arguments to javascript `String.replace`. |
|
|
|
3. The kernel-specific prettifier call is then composed from |
|
`kernel_config.prefix` + `kernel_text_string` + `kernel_config.postfix` and |
|
sent to the kernel for execution. This kernel call is expected to get the |
|
formatted cell text _printed_ as a json-compatible string. Since most |
|
kernel languages have json packages, this should hopefully be easy to |
|
arrange. The reason for the printing text rather than simply displaying it, |
|
is that it prevents us having to translate from a kernel string |
|
representing a json string. |
|
|
|
4. The callback for the kernel execution in client-side javascript parses the |
|
printed json-format string, optionally trims trailing whitespace according |
|
to the `trim_formatted_text` key (which defaults to `true`) in the |
|
per-kernel config, and then sets the cell text using the result. |
|
|
|
The process is probably best illustrated using an example for the python |
|
implementation in `code_prettify`: |
|
|
|
1. **At nbextension load**, the `code_prettify.kernel_config_map_json` config |
|
option is parsed to give the json object |
|
|
|
```json |
|
{ |
|
"python": { |
|
"library": "import json\nimport yapf.yapflib.yapf_api", |
|
"prefix": "print(json.dumps(yapf.yapflib.yapf_api.FormatCode(u", |
|
"postfix": ")[0]))" |
|
} |
|
} |
|
``` |
|
|
|
(other kernel languages are omitted for clarity). |
|
|
|
2. **On kernel becoming ready**, the nbextension looks up the config for the |
|
kernel's language (in our example, this is the `python` key of the kernel |
|
config json object above). It then sends the kernel config's `library` |
|
string to the kernel for execution. Thus the python implementation above |
|
executes |
|
|
|
```python |
|
import json |
|
import yapf.yapflib.yapf_api |
|
``` |
|
|
|
3. **On requesting a cell be prettified** which can happen by clicking the |
|
toolbar, or with a (configurable) hotkey, the following happens: |
|
|
|
Say the cell to be formatted contains the following ugly python code: |
|
|
|
```python |
|
msg= 'hello '+"world" |
|
print ( |
|
msg ) |
|
``` |
|
|
|
Then the result of the `JSON.stringify` call will be a string containing |
|
|
|
```json |
|
"msg= 'hello '+\"world\"\nprint (\n msg )" |
|
``` |
|
|
|
(note the opening and closing quotes). Concatenating this with the prefix & |
|
postfix strings from the python kernel config above, gives us the kernel |
|
code to execute. The call sent to the python kernel is therefore |
|
|
|
```python |
|
print(json.dumps(yapf.yapflib.yapf_api.FormatCode(u"msg= 'hello '+\"world\"\nprint (\n msg )")[0])) |
|
``` |
|
|
|
4. What gets 'printed' by the kernel (i.e. returned to the javascript stream |
|
callback) is the following json-format string: |
|
|
|
```json |
|
"msg = 'hello ' + \"world\"\nprint(msg)\n" |
|
``` |
|
|
|
The default is to trim whitepace from the returned prettified text, which |
|
results in the final prettified python code for the cell: |
|
|
|
```python |
|
msg = 'hello ' + "world" |
|
print(msg) |
|
``` |
|
|
|
|
|
Defining a new plugin |
|
--------------------- |
|
|
|
As an example, we will add a new plugin which reformats code using the |
|
[autopep8] module in python, rather than the [yapf] library used by |
|
`code_prettify`. Such a plugin, [jupyter-autopep8] was developed by [@kenkoooo] |
|
as a fork of an old version of `code_prettify`. Redefining it here has the |
|
advantage of using the updated and more-robust architecture, in addition to |
|
making it possible to reformat the whole notebook in one go. |
|
|
|
For this new nbextension, we just have to run `import autopep8` as the kernel |
|
library code, and then call the `autopep8.fix_code` function on cells' text. |
|
Hence what we have to do is: |
|
|
|
- copy `code_prettify.js` to `autopep8.js` |
|
|
|
- update `mod_name`, `hotkeys`, `button_icon` default config values in the new |
|
`autopep8.js`. Also update the `cfg.kernel_config_map` value to use the |
|
correct kernel code: |
|
```javascript |
|
cfg.kernel_config_map = { // map of options for supported kernels |
|
"python": { |
|
"library": "import json\nimport autopep8", |
|
"prefix": "print(json.dumps(autopep8.fix_code(u", |
|
"postfix": ")))" |
|
} |
|
}; |
|
``` |
|
|
|
- copy `code_prettify.yaml` to `autopep8.yaml`, and update its values (name, |
|
require, readme, plus the new defaults for hotkeys, icon, and |
|
kernel_config_map |
|
|
|
- that's all :-) |
|
|
|
Of course, for this simple case, one could equally have just updated the |
|
configuration of `code_prettify` using the [jupyter_nbextensions_configurator] |
|
to use [autopep8] instead of [yapf] to reformat the python code. |
|
But, if you want two alternative prettifiers available for the same kernel |
|
language, we need to define separate plugins. |
|
|
|
Custom Yapf Styles |
|
------------------ |
|
|
|
Using the default `yapf` engine, one may define a custom formatting style according to the [package documentation](https://github.com/google/yapf#formatting-style). |
|
|
|
The `code_prettify` extension is configured to follow the default `yapf` ordering (minus the command line option) and will search for the formatting style in the following manner: |
|
|
|
> 1. In the [style] section of a .style.yapf file in either the current directory or one of its parent directories. |
|
> 2. In the [yapf] section of a setup.cfg file in either the current directory or one of its parent directories. |
|
> 3. In the ~/.config/yapf/style file in your home directory. |
|
> |
|
> If none of those files are found, the default style is used (PEP8). |
|
|
|
This means that one can set up a globa custom yapf style using `~/.config/yapf/style` or a project-specific one using the project directory. |
|
|
|
History |
|
------- |
|
|
|
- [@jfbercher], august 14, 2016, first version, named `yapf_ext` |
|
- [@jfbercher], august 19, 2016, second version `code_prettify` |
|
- introduced support for R and javascript. |
|
- changed extension name from `yapf_ext` to `code_prettify` |
|
- [@jcb91], december 2016 |
|
- made addition of toolbar button & hotkey configurable |
|
- reworked to avoid regex replacements for conversion to/from kernel string |
|
formats, in favour of json-string interchange |
|
- made kernel-specific prettifier calls configurable, allowing support for |
|
different prettifiers & arbitrary kernels |
|
- improved documentation |
|
- [@jfbercher], december 2016-january 2017 |
|
- added a configurable shortkey to reflow the whole notebook |
|
- extracted most of the code to build a general library of functions, |
|
`kernel_exec_on_cell.js`, which can be used for all nbextensions which |
|
needs to exec some code (via the current kernel) on the text from cells. |
|
- added 2to3 as a plugin to the shared library |
|
- [@jcb91], january 2017 |
|
- library: Use actions to avoid problems with auto-generated actions |
|
generated by keyboard_manager, which were overwriting each other. |
|
Also fix toolbar button removal. |
|
- [@jfbercher], january 2017 |
|
- updated documentation |
|
- added autopep8 nbextension as a plugin using the shared library |
|
- [@artificialsoph], Jan 2018 |
|
- updated documentation |
|
- changed default behavior to load custom yapf styles |
|
- [@jfbercher], April 2019 |
|
- corrected an issue in configs merge |
|
- added an option for displaying an alert if kernel is not supported and turned it off by default (instead issue a warning in the js console). |
|
|
|
[2to3]: README_2to3.md |
|
[@jcb91]: https://github.com/jcb91 |
|
[@jfbercher]: https://github.com/jfbercher |
|
[@kenkoooo]: https://github.com/kenkoooo |
|
[autopep8]: https://github.com/hhatto/autopep8 |
|
[code-prettify]: README_code_prettify.md |
|
[jupyter-autopep8]: README_autopep8.md |
|
[fontawesome]: https://fontawesome.com/icons |
|
[internals]: #Internals |
|
[jupyter-autopep8]: https://github.com/kenkoooo/jupyter-autopep8 |
|
[jupyter_nbextensions_configurator]: https://github.com/Jupyter-contrib/jupyter_nbextensions_configurator |
|
[yapf]: https://github.com/google/yapf |
|
|