diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 829475a2c..5e108dc57 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -61,6 +61,33 @@ from any directory in your system with::
jupyter notebook
+Verification
+^^^^^^^^^^^^
+
+While running the notebook, select one of your notebook files (the file will have the extension ``.ipynb``).
+In the top tab you will click on "Help" and then click on "About". In the pop window you will see information about the version of Jupyter that you are running. You will see "The version of the notebook server is:".
+If you are working in development mode, you will see that your version of Jupyter notebook will include the word "dev".
+
+.. image:: ./docs/source/_static/images/jupyter-verification.png
+ :width: 40pt
+
+If it does not include the word "dev", you are currently not working in development mode and should follow the steps below to uninstall and reinstall Jupyter.
+
+Troubleshooting the Installation
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you do not see that your Jupyter Notebook is not running on dev mode, it's possible that you are
+running other instances of Jupyter Notebook. You can try the following steps:
+
+1. Uninstall all instances of the notebook package. These include any installations you made using
+ pip or conda
+2. Run ``python3 -m pip install -e .`` in the notebook repository to install the notebook from there
+3. Run ``npm run build`` to make sure the Javascript and CSS are updated and compiled
+4. Launch with ``python3 -m notebook --port 8989``, and check that the browser is pointing to ``localhost:8989``
+ (rather than the default 8888). You don't necessarily have to launch with port 8989, as long as you use
+ a port that is neither the default nor in use, then it should be fine.
+5. Verify the installation with the steps in the previous section.
+
Rebuilding JavaScript and CSS
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/bower.json b/bower.json
index e7d619ad3..9297c1a47 100644
--- a/bower.json
+++ b/bower.json
@@ -15,7 +15,7 @@
"jquery-ui": "components/jqueryui#~1.10",
"marked": "~0.3",
"MathJax": "components/MathJax#~2.6",
- "moment": "~2.8.4",
+ "moment": "~2.19.3",
"preact": "https://unpkg.com/preact@^7.2.0/dist/preact.min.js",
"preact-compat": "https://unpkg.com/preact-compat@^3.14.3/dist/preact-compat.min.js",
"proptypes": "https://unpkg.com/proptypes@^0.14.4/index.js",
diff --git a/docs/source/_static/images/blank-notebook-ui.png b/docs/source/_static/images/blank-notebook-ui.png
new file mode 100644
index 000000000..f49762636
Binary files /dev/null and b/docs/source/_static/images/blank-notebook-ui.png differ
diff --git a/docs/source/_static/images/blank-notebook-ui.svg b/docs/source/_static/images/blank-notebook-ui.svg
new file mode 100644
index 000000000..10f5ab4fd
--- /dev/null
+++ b/docs/source/_static/images/blank-notebook-ui.svg
@@ -0,0 +1,397 @@
+
+
+
+
diff --git a/docs/source/_static/images/jupyter-verification.png b/docs/source/_static/images/jupyter-verification.png
new file mode 100644
index 000000000..da816450e
Binary files /dev/null and b/docs/source/_static/images/jupyter-verification.png differ
diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst
index 4a4bb42fc..ebc7d0861 100644
--- a/docs/source/changelog.rst
+++ b/docs/source/changelog.rst
@@ -1,7 +1,7 @@
.. _changelog:
-Jupyter notebook changelog
-==========================
+Changelog
+=========
A summary of changes in the Jupyter notebook.
For more detailed information, see
@@ -21,6 +21,137 @@ We strongly recommend that you upgrade to version 9+ of pip before upgrading ``n
Use ``pip install pip --upgrade`` to upgrade pip. Check pip version with
``pip --version``.
+.. _release-5.5.0:
+
+5.5.0
+-----
+
+New features:
+
+- The files list now shows file sizes (:ghpull:`3539`)
+- Add a quit button in the dashboard (:ghpull:`3004`)
+- Display hostname in the terminal when running remotely (:ghpull:`3356`)
+- Add slides exportation/download to the menu (:ghpull:`3287`)
+- Add any extra installed nbconvert exporters to the "Download as" menu (:ghpull:`3323`)
+- Editor: warning when overwriting a file that is modified on disk (:ghpull:`2783`)
+- Display a warning message if cookies are not enabled (:ghpull:`3511`)
+- Basic ``__version__`` reporting for extensions (:ghpull:`3541`)
+- Add ``NotebookApp.terminals_enabled`` config option (:ghpull:`3478`)
+- Make buffer time between last modified on disk and last modified on last save configurable (:ghpull:`3273`)
+- Allow binding custom shortcuts for 'close and halt' (:ghpull:`3314`)
+- Add description for 'Trusted' notification (:ghpull:`3386`)
+- Add ``settings['activity_sources']`` (:ghpull:`3401`)
+- Add an ``output_updated.OutputArea`` event (:ghpull:`3560`)
+
+
+Fixing problems:
+
+- Fixes to improve web accessibility (:ghpull:`3507`)
+
+ - There is more to do on this! See :ghissue:`1801`.
+
+- Fixed color contrast issue in tree.less (:ghpull:`3336`)
+- Allow cancelling upload of large files (:ghpull:`3373`)
+- Don't clear login cookie on requests without cookie (:ghpull:`3380`)
+- Don't trash files on different device to home dir on Linux (:ghpull:`3304`)
+- Clear waiting asterisks when restarting kernel (:ghpull:`3494`)
+- Fix output prompt when ``execution_count`` missing (:ghpull:`3236`)
+- Make the 'changed on disk' dialog work when displayed twice (:ghpull:`3589`)
+- Fix going back to root directory with history in notebook list (:ghpull:`3411`)
+- Allow defining keyboard shortcuts for missing actions (:ghpull:`3561`)
+- Prevent default on pageup/pagedown when completer is active (:ghpull:`3500`)
+- Prevent default event handling on new terminal (:ghpull:`3497`)
+- ConfigManager should not write out default values found in the .d directory (:ghpull:`3485`)
+- Fix leak of iopub object in activity monitoring (:ghpull:`3424`)
+- Javascript lint in notebooklist.js (:ghpull:`3409`)
+- Some Javascript syntax fixes (:ghpull:`3294`)
+- Convert native for loop to ``Array.forEach()`` (:ghpull:`3477`)
+- Disable cache when downloading nbconvert output (:ghpull:`3484`)
+- Add missing digestmod arg to HMAC (:ghpull:`3399`)
+- Log OSErrors failing to create less-critical files during startup (:ghpull:`3384`)
+- Use powershell on Windows (:ghpull:`3379`)
+- API spec improvements, API handler improvements (:ghpull:`3368`)
+- Set notebook to dirty state after change to kernel metadata (:ghpull:`3350`)
+- Use CSP header to treat served files as belonging to a separate origin (:ghpull:`3341`)
+- Don't install gettext into builtins (:ghpull:`3330`)
+- Add missing ``import _`` (:ghpull:`3316`, :ghpull:`3326`)
+- Write ``notebook.json`` file atomically (:ghpull:`3305`)
+- Fix clicking with modifiers, page title updates (:ghpull:`3282`)
+- Upgrade jQuery to version 2.2 (:ghpull:`3428`)
+- Upgrade xterm.js to 3.1.0 (:ghpull:`3189`)
+- Upgrade moment.js to 2.19.3 (:ghpull:`3562`)
+- Upgrade CodeMirror to 5.35 (:ghpull:`3372`)
+- "Require" pyzmq>=17 (:ghpull:`3586`)
+
+Documentation:
+
+- Documentation updates and organisation (:ghpull:`3584`)
+- Add section in docs about privacy (:ghpull:`3571`)
+- Add explanation on how to change the type of a cell to Markdown (:ghpull:`3377`)
+- Update docs with confd implementation details (:ghpull:`3520`)
+- Add more information for where ``jupyter_notebook_config.py`` is located (:ghpull:`3346`)
+- Document options to enable nbextensions in specific sections (:ghpull:`3525`)
+- jQuery attribute selector value MUST be surrounded by quotes (:ghpull:`3527`)
+- Do not execute special notebooks with nbsphinx (:ghpull:`3360`)
+- Other minor fixes in :ghpull:`3288`, :ghpull:`3528`, :ghpull:`3293`, :ghpull:`3367`
+
+Testing:
+
+- Testing with Selenium & Sauce labs (:ghpull:`3321`)
+- Selenium utils + markdown rendering tests (:ghpull:`3458`)
+- Convert insert cell tests to Selenium (:ghpull:`3508`)
+- Convert prompt numbers tests to Selenium (:ghpull:`3554`)
+- Convert delete cells tests to Selenium (:ghpull:`3465`)
+- Convert undelete cell tests to Selenium (:ghpull:`3475`)
+- More selenium testing utilities (:ghpull:`3412`)
+- Only check links when build is trigger by Travis Cron job (:ghpull:`3493`)
+- Fix Appveyor build errors (:ghpull:`3430`)
+- Undo patches in teardown before attempting to delete files (:ghpull:`3459`)
+- Get tests running with tornado 5 (:ghpull:`3398`)
+- Unpin ipykernel version on Travis (:ghpull:`3223`)
+
+Thanks to the following contributors:
+
+- Arovit Narula (`arovit `__)
+- Ashley Teoh (`ashleytqy `__)
+- Nicholas Bollweg (`bollwyvl `__)
+- Alex Rothberg (`cancan101 `__)
+- Celina Kilcrease (`ckilcrease `__)
+- dabuside (`dabuside `__)
+- Damian Avila (`damianavila `__)
+- Dana Lee (`danagilliann `__)
+- Dave Hirschfeld (`dhirschfeld `__)
+- Heng GAO (`ehengao `__)
+- Leo Gallucci (`elgalu `__)
+- Evan Van Dam (`evandam `__)
+- forbxy (`forbxy `__)
+- Grant Nestor (`gnestor `__)
+- Ethan T. Hendrix (`hendrixet `__)
+- Miro Hrončok (`hroncok `__)
+- Paul Ivanov (`ivanov `__)
+- Darío Hereñú (`kant `__)
+- Kevin Bates (`kevin-bates `__)
+- Maarten Breddels (`maartenbreddels `__)
+- Michael Droettboom (`mdboom `__)
+- Min RK (`minrk `__)
+- M Pacer (`mpacer `__)
+- Peter Parente (`parente `__)
+- Paul Masson (`paulmasson `__)
+- Philipp Rudiger (`philippjfr `__)
+- Mac Knight (`Shels1909 `__)
+- Hisham Elsheshtawy (`Sheshtawy `__)
+- Simon Biggs (`SimonBiggs `__)
+- Sunil Hari (`sunilhari `__)
+- Thomas Kluyver (`takluyver `__)
+- Tim Klever (`tklever `__)
+- Gabriel Ruiz (`unnamedplay-r `__)
+- Vaibhav Sagar (`vaibhavsagar `__)
+- William Hosford (`whosford `__)
+- Hong (`xuhdev `__)
+
+See the 5.5 milestone on GitHub for a complete list of
+`pull requests `__ involved in this release.
+
.. _release-5.4.1:
5.4.1
diff --git a/docs/source/examples/Notebook/Distributing Jupyter Extensions as Python Packages.ipynb b/docs/source/examples/Notebook/Distributing Jupyter Extensions as Python Packages.ipynb
index 43e2d1b54..2f95bc5be 100644
--- a/docs/source/examples/Notebook/Distributing Jupyter Extensions as Python Packages.ipynb
+++ b/docs/source/examples/Notebook/Distributing Jupyter Extensions as Python Packages.ipynb
@@ -31,7 +31,7 @@
"metadata": {},
"source": [
"### Why create a Python package for Jupyter extensions?\n",
- "Since it is rare to have a server extension that does not have any frontend components (an nbextension), for convenience and consistency, all these client and server extensions with their assets can be packaged and versioned together as a Python package with a few simple commands. This makes installing the package of extensions easier and less error-prone for the user. "
+ "Since it is rare to have a server extension that does not have any frontend components (an nbextension), for convenience and consistency, all these client and server extensions with their assets can be packaged and versioned together as a Python package with a few simple commands, or as of Notebook 5.3, handled automatically by your package manager of choice. This makes installing the package of extensions easier and less error-prone for the user."
]
},
{
@@ -52,6 +52,16 @@
"```"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Automatic installation and Enabling\n",
+ "> New in Notebook 5.3\n",
+ "\n",
+ "The absolute simplest case requires no user interaction at all! Configured correctly, after installing with their package manager of choice, both server and frontend extensions can be enabled by default in the environment where they were installed, i.e. `--sys-prefix`. See the `setup.py` in the example below."
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -259,6 +269,130 @@
"```"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Automatically enabling a server extension and nbextension\n",
+ "> New in Notebook 5.3\n",
+ "\n",
+ "Server extensions and nbextensions can be installed and enabled without any user intervention or post-install scripts beyond ` install `"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In addition to the `my_fancy_module` file tree, assume:\n",
+ "- `jupyter-config/`\n",
+ " - `jupyter_notebook_config.d/`\n",
+ " - `my_fancy_module.json`\n",
+ " - `nbconfig/`\n",
+ " - `notebook.d/`\n",
+ " - `my_fancy_module.json`"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### `jupyter-config/jupyter_notebook_config.d/my_fancy_module.json`\n",
+ "```json\n",
+ "{\n",
+ " \"NotebookApp\": {\n",
+ " \"nbserver_extensions\": {\n",
+ " \"my_fancy_module\": true\n",
+ " }\n",
+ " }\n",
+ "}\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### `jupyter-config/nbconfig/notebook.d/my_fancy_module.json`\n",
+ "```json\n",
+ "{\n",
+ " \"load_extensions\": {\n",
+ " \"my_fancy_module/index\": true\n",
+ " }\n",
+ "}\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Put all of them in place via:\n",
+ "\n",
+ "#### `setup.py`\n",
+ "```python\n",
+ "import setuptools\n",
+ "\n",
+ "setuptools.setup(\n",
+ " name=\"MyFancyModule\",\n",
+ " ...\n",
+ " include_package_data=True,\n",
+ " data_files=[\n",
+ " # like `jupyter nbextension install --sys-prefix`\n",
+ " (\"share/jupyter/nbextensions/my_fancy_module\", [\n",
+ " \"my_fancy_module/static/index.js\",\n",
+ " ]),\n",
+ " # like `jupyter nbextension enable --sys-prefix`\n",
+ " (\"etc/jupyter/nbconfig/notebook.d\", [\n",
+ " \"jupyter-config/nbconfig/notebook.d/my_fancy_module.json\"\n",
+ " ]),\n",
+ " # like `jupyter serverextension enable --sys-prefix`\n",
+ " (\"etc/jupyter/jupyter_notebook_config.d\", [\n",
+ " \"jupyter-config/jupyter_notebook_config.d/my_fancy_module.json\"\n",
+ " ])\n",
+ " ],\n",
+ " ...\n",
+ " zip_safe=False\n",
+ ")\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "and last, but not least:\n",
+ "\n",
+ "#### `MANIFEST.in`\n",
+ "```config\n",
+ "recursive-include jupyter-config *.json\n",
+ "recursive-include my_fancy_module/static *.js\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As most package managers will only modify their environment, the eventual configuration will be as if the user had typed:\n",
+ "```\n",
+ "jupyter nbextension install --py my_fancy_module --sys-prefix\n",
+ "jupyter nbextension enable --py my_fancy_module --sys-prefix\n",
+ "jupyter serverextension enable --py my_fancy_module --sys-prefix\n",
+ "```\n",
+ "\n",
+ "If a user manually `disable`s an extension, that configuration will override the bundled package configuration."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### When automagical install fails\n",
+ "Note this can still fail in certain situations with `pip`, requiring manual use of `install` and `enable` commands.\n",
+ "\n",
+ "Non-python-specific package managers (e.g. `conda`, `apt`) may choose not to implement the above behavior at the `setup.py` level, having more ways to put data files in various places at build time."
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -368,7 +502,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.5.1"
+ "version": "3.6.5"
}
},
"nbformat": 4,
diff --git a/docs/source/examples/Notebook/examples_index.rst b/docs/source/examples/Notebook/examples_index.rst
index 7d846f739..aa7da3ffe 100644
--- a/docs/source/examples/Notebook/examples_index.rst
+++ b/docs/source/examples/Notebook/examples_index.rst
@@ -1,14 +1,13 @@
-`View the original notebooks on nbviewer`__
+=================
+Notebook Examples
+=================
+
+The pages in this section are all converted notebook files. You can also
+`view these notebooks on nbviewer`__.
__ http://nbviewer.jupyter.org/github/jupyter/notebook/blob/master/
docs/source/examples/Notebook/
-========
-Examples
-========
-
-The following notebooks have been rendered for your convenience.
-
.. toctree::
:maxdepth: 2
diff --git a/docs/source/index.rst b/docs/source/index.rst
index f44bf8514..d61dcac31 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -1,20 +1,23 @@
====================
-The Jupyter notebook
+The Jupyter Notebook
====================
+* `Installation `_
+* `Starting the Notebook `_
+
.. toctree::
:maxdepth: 1
:caption: User Documentation
notebook
- Installation
- Running the Notebook
- Migrating from IPython
ui_components
+ examples/Notebook/examples_index.rst
+ troubleshooting
+ changelog
comms
.. toctree::
- :maxdepth: 2
+ :maxdepth: 1
:caption: Configuration
config_overview
@@ -33,38 +36,8 @@ The Jupyter notebook
development_release
development_faq
-.. toctree::
- :maxdepth: 1
- :caption: Community documentation
-
- examples/Notebook/examples_index.rst
- examples/Notebook/What is the Jupyter Notebook
- examples/Notebook/Notebook Basics
- examples/Notebook/Running Code
- examples/Notebook/Working With Markdown Cells
- examples/Notebook/Custom Keyboard Shortcuts
- examples/Notebook/JavaScript Notebook Extensions
- examples/Notebook/Importing Notebooks
- examples/Notebook/Connecting with the Qt Console
- examples/Notebook/Typesetting Equations
-
.. toctree::
:hidden:
examples/Notebook/nbpackage/mynotebook.ipynb
examples/Notebook/nbpackage/nbs/other.ipynb
-
-.. toctree::
- :maxdepth: 2
- :caption: About Jupyter Notebook
-
- changelog
-
-.. toctree::
- :maxdepth: 1
- :caption: Questions? Suggestions?
-
- Jupyter mailing list
- Jupyter website
- Stack Overflow - Jupyter
- Stack Overflow - Jupyter-notebook
diff --git a/docs/source/notebook.rst b/docs/source/notebook.rst
index 4ddef27d7..fdfa1182d 100644
--- a/docs/source/notebook.rst
+++ b/docs/source/notebook.rst
@@ -83,6 +83,29 @@ without relying on nbviewer.
:ref:`Details on the notebook JSON file format `
+Notebooks and privacy
+~~~~~~~~~~~~~~~~~~~~~
+
+Because you use Jupyter in a web browser, some people are understandably
+concerned about using it with sensitive data.
+However, if you followed the standard
+`install instructions `_,
+Jupyter is actually running on your own computer.
+If the URL in the address bar starts with ``http://localhost:`` or
+``http://127.0.0.1:``, it's your computer acting as the server.
+Jupyter doesn't send your data anywhere else—and as it's open source,
+other people can check that we're being honest about this.
+
+You can also use Jupyter remotely:
+your company or university might run the server for you, for instance.
+If you want to work with sensitive data in those cases,
+talk to your IT or data protection staff about it.
+
+We aim to ensure that other pages in your browser or other users on the same
+computer can't access your notebook server. See :ref:`server_security` for
+more about this.
+
+
Starting the notebook server
----------------------------
@@ -177,32 +200,24 @@ Notebook user interface
-----------------------
When you create a new notebook document, you will be presented with the
-**notebook name**, a **menu bar**, a **toolbar** and an empty **code
-cell**.
+**notebook name**, a **menu bar**, a **toolbar** and an empty **code cell**.
-**notebook name**: The name of the notebook document is displayed at the top
-of the page, next to the ``IP[y]: Notebook`` logo. This name reflects the name
-of the ``.ipynb`` notebook document file. Clicking on the notebook name
-brings up a dialog which allows you to rename it. Thus, renaming a notebook
+.. image:: ./_static/images/blank-notebook-ui.png
+
+**Notebook name**: The name displayed at the top of the page,
+next to the Jupyter logo, reflects the name of the ``.ipynb`` file.
+Clicking on the notebook name brings up a dialog which allows you to rename it.
+Thus, renaming a notebook
from "Untitled0" to "My first notebook" in the browser, renames the
``Untitled0.ipynb`` file to ``My first notebook.ipynb``.
-**menu bar**: The menu bar presents different options that may be used to
+**Menu bar**: The menu bar presents different options that may be used to
manipulate the way the notebook functions.
-**toolbar**: The tool bar gives a quick way of performing the most-used
+**Toolbar**: The tool bar gives a quick way of performing the most-used
operations within the notebook, by clicking on an icon.
-**code cell**: the default type of cell, read on for an explanation of cells
-
-.. note::
-
- As of notebook version 4.1, the user interface allows for multiple cells to
- be selected. The ``quick celltype selector``, found in the menubar, will
- display a dash ``-`` when multiple cells are selected to indicate that the
- type of the cells in the selection might not be unique. The quick selector
- can still be used to change the type of the selection and will change the
- type of all the currently selected cells.
+**Code cell**: the default type of cell; read on for an explanation of cells.
Structure of a notebook document
@@ -210,7 +225,7 @@ Structure of a notebook document
The notebook consists of a sequence of cells. A cell is a multiline text input
field, and its contents can be executed by using :kbd:`Shift-Enter`, or by
-clicking either the "Play" button the toolbar, or `Cell | Run` in the menu bar.
+clicking either the "Play" button the toolbar, or :guilabel:`Cell`, :guilabel:`Run` in the menu bar.
The execution behavior of a cell is determined by the cell's type. There are three
types of cells: **code cells**, **markdown cells**, and **raw cells**. Every
cell starts off being a **code cell**, but its type can be changed by using a
@@ -224,9 +239,8 @@ see the `collection of examples
Code cells
~~~~~~~~~~
A *code cell* allows you to edit and write new code, with full syntax
-highlighting and tab completion. By default, the language associated to a code
-cell is Python, but other languages, such as ``Julia`` and ``R``, can be
-handled using :ref:`cell magic commands `.
+highlighting and tab completion. The programming language you use depends
+on the *kernel*, and the default kernel (IPython) runs Python code.
When a code cell is executed, code that it contains is sent to the kernel
associated with the notebook. The results that are returned from this
@@ -269,7 +283,7 @@ supports a `large subset `_ of LaTeX functionality
.. _mathjax_tex: https://docs.mathjax.org/en/latest/tex.html
Standard mathematics environments defined by LaTeX and AMS-LaTeX (the
-`amsmath` package) also work, such as
+``amsmath`` package) also work, such as
``\begin{equation}...\end{equation}``, and ``\begin{align}...\end{align}``.
New LaTeX macros may be defined using standard methods,
such as ``\newcommand``, by placing them anywhere *between math delimiters* in
@@ -286,7 +300,7 @@ Raw cells
*Raw* cells provide a place in which you can write *output* directly.
Raw cells are not evaluated by the notebook.
When passed through nbconvert_, raw cells arrive in the
-destination format unmodified. For example, this allows you to type full LaTeX
+destination format unmodified. For example, you can type full LaTeX
into a raw cell, which will only be rendered by LaTeX after conversion by
nbconvert.
@@ -306,17 +320,14 @@ correctly. This is much more convenient for interactive exploration than
breaking up a computation into scripts that must be executed together, as was
previously necessary, especially if parts of them take a long time to run.
-At certain moments, it may be necessary to interrupt a calculation which is
-taking too long to complete. This may be done with the `Kernel | Interrupt`
-menu option, or the :kbd:`Ctrl-m i` keyboard shortcut.
-Similarly, it may be necessary or desirable to restart the whole computational
-process, with the `Kernel | Restart` menu option or :kbd:`Ctrl-m .`
+To interrupt a calculation which is taking too long, use the :guilabel:`Kernel`,
+:guilabel:`Interrupt` menu option, or the :kbd:`i,i` keyboard shortcut.
+Similarly, to restart the whole computational process,
+use the :guilabel:`Kernel`, :guilabel:`Restart` menu option or :kbd:`0,0`
shortcut.
-A notebook may be downloaded in either a ``.ipynb`` or ``.py`` file from the
-menu option `File | Download as`. Choosing the ``.py`` option downloads a
-Python ``.py`` script, in which all rich output has been removed and the
-content of markdown cells have been inserted as comments.
+A notebook may be downloaded as a ``.ipynb`` file or converted to a number of
+other formats using the menu option :guilabel:`File`, :guilabel:`Download as`.
.. seealso::
@@ -333,31 +344,16 @@ shortcuts are also available for the most common ones. The essential shortcuts
to remember are the following:
* :kbd:`Shift-Enter`: run cell
- Execute the current cell, show output (if any), and jump to the next cell
- below. If :kbd:`Shift-Enter` is invoked on the last cell, a new code
- cell will also be created. Note that in the notebook, typing :kbd:`Enter`
- on its own *never* forces execution, but rather just inserts a new line in
- the current cell. :kbd:`Shift-Enter` is equivalent to clicking the
- ``Cell | Run`` menu item.
-
-* :kbd:`Ctrl-Enter`: run cell in-place
- Execute the current cell as if it were in "terminal mode", where any
- output is shown, but the cursor *remains* in the current cell. The cell's
- entire contents are selected after execution, so you can just start typing
- and only the new input will be in the cell. This is convenient for doing
- quick experiments in place, or for querying things like filesystem
- content, without needing to create additional cells that you may not want
- to be saved in the notebook.
-
-* :kbd:`Alt-Enter`: run cell, insert below
- Executes the current cell, shows the output, and inserts a *new*
- cell between the current cell and the cell below (if one exists). This
- is thus a shortcut for the sequence :kbd:`Shift-Enter`, :kbd:`Ctrl-m a`.
- (:kbd:`Ctrl-m a` adds a new cell above the current one.)
-
-* :kbd:`Esc` and :kbd:`Enter`: Command mode and edit mode
- In command mode, you can easily navigate around the notebook using keyboard
- shortcuts. In edit mode, you can edit text in cells.
+ Execute the current cell, show any output, and jump to the next cell below.
+ If :kbd:`Shift-Enter` is invoked on the last cell, it makes a new cell below.
+ This is equivalent to clicking the :guilabel:`Cell`, :guilabel:`Run` menu
+ item, or the Play button in the toolbar.
+
+* :kbd:`Esc`: Command mode
+ In command mode, you can navigate around the notebook using keyboard shortcuts.
+
+* :kbd:`Enter`: Edit mode
+ In edit mode, you can edit text in cells.
For the full list of available shortcuts, click :guilabel:`Help`,
:guilabel:`Keyboard Shortcuts` in the notebook menus.
@@ -375,64 +371,49 @@ Installing kernels
For information on how to install a Python kernel, refer to the
`IPython install page `__.
-Kernels for other languages can be found in the `IPython wiki
-`_.
-They usually come with instruction what to run to make the kernel available
+The Jupyter wiki has a long list of `Kernels for other languages
+`_.
+They usually come with instructions on how to make the kernel available
in the notebook.
.. _signing_notebooks:
-Signing Notebooks
------------------
+Trusting Notebooks
+------------------
To prevent untrusted code from executing on users' behalf when notebooks open,
-we have added a signature to the notebook, stored in metadata.
+we store a signature of each trusted notebook.
The notebook server verifies this signature when a notebook is opened.
-If the signature stored in the notebook metadata does not match,
-javascript and HTML output will not be displayed on load,
-and must be regenerated by re-executing the cells.
+If no matching signature is found,
+Javascript and HTML output will not be displayed
+until they are regenerated by re-executing the cells.
-Any notebook that you have executed yourself *in its entirety* will be
-considered trusted, and its HTML and javascript output will be displayed on
+Any notebook that you have fully executed yourself will be
+considered trusted, and its HTML and Javascript output will be displayed on
load.
If you need to see HTML or Javascript output without re-executing,
-you can explicitly trust notebooks, such as those shared with you,
-or those that you have written yourself prior to IPython 2.0,
+and you are sure the notebook is not malicious, you can tell Jupyter to trust it
at the command-line with::
- $ jupyter trust mynotebook.ipynb [other notebooks.ipynb]
-
-This just generates a new signature stored in each notebook.
-
-You can generate a new notebook signing key with::
-
- $ jupyter trust --reset
+ $ jupyter trust mynotebook.ipynb
-.. include:: links.txt
+See :ref:`notebook_security` for more details about the trust mechanism.
Browser Compatibility
---------------------
-The Jupyter Notebook is officially supported by the latest stable versions of the
-following browsers:
+The Jupyter Notebook aims to support the latest versions of these browsers:
* Chrome
* Safari
* Firefox
-The is mainly due to the notebook's usage of WebSockets and the flexible box
-model.
-
-The following browsers are unsupported:
-
-* Safari < 5
-* Firefox < 6
-* Chrome < 13
-* Opera (any): CSS issues, but execution might work
-* Internet Explorer < 10
-* Internet Explorer ≥ 10 (same as Opera)
+Up to date versions of Opera and Edge may also work, but if they don't, please
+use one of the supported browsers.
Using Safari with HTTPS and an untrusted certificate is known to not work
(websockets will fail).
+
+.. include:: links.txt
diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst
new file mode 100644
index 000000000..306234d7b
--- /dev/null
+++ b/docs/source/troubleshooting.rst
@@ -0,0 +1,87 @@
+What to do when things go wrong
+===============================
+
+First, have a look at the common problems listed below. If you can figure it out
+from these notes, it will be quicker than asking for help.
+
+Check that you have the latest version of any packages that look relevant.
+Unfortunately it's not always easy to figure out what packages are relevant,
+but if there was a bug that's already been fixed,
+it's easy to upgrade and get on with what you wanted to do.
+
+Jupyter fails to start
+----------------------
+
+* Have you `installed it `__? ;-)
+* If you're using a menu shortcut or Anaconda launcher to start it, try
+ opening a terminal or command prompt and running the command ``jupyter notebook``.
+* If it can't find ``jupyter``,
+ you may need to configure your ``PATH`` environment variable.
+ If you don't know what that means, and don't want to find out,
+ just (re)install Anaconda with the default settings,
+ and it should set up PATH correctly.
+* If Jupyter gives an error that it can't find ``notebook``,
+ check with pip or conda that the ``notebook`` package is installed.
+* Try running ``jupyter-notebook`` (with a hyphen). This should normally be the
+ same as ``jupyter notebook`` (with a space), but if there's any difference,
+ the version with the hyphen is the 'real' launcher, and the other one wraps
+ that.
+
+Jupyter doesn't load or doesn't work in the browser
+---------------------------------------------------
+
+* Try in another browser (e.g. if you normally use Firefox, try with Chrome).
+ This helps pin down where the problem is.
+* Try disabling any browser extensions and/or any Jupyter extensions you have
+ installed.
+* Some internet security software can interfere with Jupyter.
+ If you have security software, try turning it off temporarily,
+ and look in the settings for a more long-term solution.
+* In the address bar, try changing between ``localhost`` and ``127.0.0.1``.
+ They should be the same, but in some cases it makes a difference.
+
+Jupyter can't start a kernel
+----------------------------
+
+Files called *kernel specs* tell Jupyter how to start different kinds of kernel.
+To see where these are on your system, run ``jupyter kernelspec list``::
+
+ $ jupyter kernelspec list
+ Available kernels:
+ python3 /home/takluyver/.local/lib/python3.6/site-packages/ipykernel/resources
+ bash /home/takluyver/.local/share/jupyter/kernels/bash
+ ir /home/takluyver/.local/share/jupyter/kernels/ir
+
+There's a special fallback for the Python kernel:
+if it doesn't find a real kernelspec, but it can import the ``ipykernel`` package,
+it provides a kernel which will run in the same Python environment as the notebook server.
+A path ending in ``ipykernel/resources``, like in the example above,
+is this default kernel.
+The default often does what you want,
+so if the ``python3`` kernelspec points somewhere else
+and you can't start a Python kernel,
+try deleting or renaming that kernelspec folder to expose the default.
+
+If your problem is with another kernel, not the Python one we maintain,
+you may need to look for support about that kernel.
+
+Asking for help
+---------------
+
+As with any problem, try searching to see if someone has already found an answer.
+If you can't find an existing answer, you can ask questions at:
+
+* The `jupyter-notebook tag on Stackoverflow `_
+* The `jupyter/help repository on Github `_
+* Or in an issue on another repository, if it's clear which component is
+ responsible.
+
+Don't forget to provide details. What error messages do you see?
+What platform are you on? How did you install Jupyter?
+What have you tried already?
+The ``jupyter troubleshoot`` command collects a lot of information
+about your installation, which can be useful.
+
+Remember that it's not anyone's job to help you.
+We want Jupyter to work for you,
+but we can't always help everyone individually.
diff --git a/docs/source/ui_components.rst b/docs/source/ui_components.rst
index db3b319bc..b5fc506df 100644
--- a/docs/source/ui_components.rst
+++ b/docs/source/ui_components.rst
@@ -1,5 +1,6 @@
-UI Components
-=============
+User interface components
+=========================
+
When opening bug reports or sending emails to the Jupyter mailing list, it is
useful to know the names of different UI components so that other developers
and users have an easier time helping you diagnose your problems. This section
diff --git a/notebook/config_manager.py b/notebook/config_manager.py
index ff6f486ce..584df8870 100644
--- a/notebook/config_manager.py
+++ b/notebook/config_manager.py
@@ -9,6 +9,7 @@ import glob
import io
import json
import os
+import copy
from six import PY3
from traitlets.config import LoggingConfigurable
@@ -36,9 +37,23 @@ def recursive_update(target, new):
target[k] = v
+def remove_defaults(data, defaults):
+ """Recursively remove items from dict that are already in defaults"""
+ # copy the iterator, since data will be modified
+ for key, value in list(data.items()):
+ if key in defaults:
+ if isinstance(value, dict):
+ remove_defaults(data[key], defaults[key])
+ if not data[key]: # prune empty subdicts
+ del data[key]
+ else:
+ if value == defaults[key]:
+ del data[key]
+
+
class BaseJSONConfigManager(LoggingConfigurable):
"""General JSON config manager
-
+
Deals with persisting/storing config in a json file with optionally
default values in a {section_name}.d directory.
"""
@@ -62,19 +77,22 @@ class BaseJSONConfigManager(LoggingConfigurable):
"""Returns the directory name for the section name: {config_dir}/{section_name}.d"""
return os.path.join(self.config_dir, section_name+'.d')
- def get(self, section_name):
+ def get(self, section_name, include_root=True):
"""Retrieve the config data for the specified section.
Returns the data as a dictionary, or an empty dictionary if the file
doesn't exist.
+
+ When include_root is False, it will not read the root .json file,
+ effectively returning the default values.
"""
- paths = [self.file_name(section_name)]
+ paths = [self.file_name(section_name)] if include_root else []
if self.read_directory:
pattern = os.path.join(self.directory(section_name), '*.json')
# These json files should be processed first so that the
# {section_name}.json take precedence.
# The idea behind this is that installing a Python package may
- # put a json file somewhere in the a .d directory, while the
+ # put a json file somewhere in the a .d directory, while the
# .json file is probably a user configuration.
paths = sorted(glob.glob(pattern)) + paths
self.log.debug('Paths used for configuration of %s: \n\t%s', section_name, '\n\t'.join(paths))
@@ -91,6 +109,12 @@ class BaseJSONConfigManager(LoggingConfigurable):
filename = self.file_name(section_name)
self.ensure_config_dir_exists()
+ if self.read_directory:
+ # we will modify data in place, so make a copy
+ data = copy.deepcopy(data)
+ defaults = self.get(section_name, include_root=False)
+ remove_defaults(data, defaults)
+
# Generate the JSON up front, since it could raise an exception,
# in order to avoid writing half-finished corrupted data to disk.
json_content = json.dumps(data, indent=2)
diff --git a/notebook/static/base/js/keyboard.js b/notebook/static/base/js/keyboard.js
index e07415ab3..129e217ef 100644
--- a/notebook/static/base/js/keyboard.js
+++ b/notebook/static/base/js/keyboard.js
@@ -218,18 +218,17 @@ define([
* Flatten a tree of shortcut sequences.
* use full to iterate over all the key/values of available shortcuts.
**/
- var dct = {};
- for(var key in tree){
- var value = tree[key];
+ var dct = {};
+ _.forEach(tree, function(value, key) {
if(typeof(value) === 'string'){
dct[key] = value;
} else {
var ftree=flatten_shorttree(value);
- for(var subkey in ftree){
+ _.forEach(ftree, function(v2, subkey) {
dct[key+','+subkey] = ftree[subkey];
- }
+ });
}
- }
+ });
return dct;
};
@@ -237,40 +236,39 @@ define([
ShortcutManager.prototype.get_action_shortcuts = function(name){
var ftree = flatten_shorttree(this._shortcuts);
var res = [];
- for (var sht in ftree ){
- if(ftree[sht] === name){
- res.push(sht);
+ _.forEach(ftree, function(value, key) {
+ if(value === name){
+ res.push(key);
}
- }
+ });
return res;
};
ShortcutManager.prototype.get_action_shortcut = function(name){
- var ftree = flatten_shorttree(this._shortcuts);
- for (var sht in ftree ){
- if(ftree[sht] === name){
- return sht;
- }
+ var matches = this.get_action_shortcuts(name);
+ if (matches.length > 0) {
+ return matches[0];
}
return undefined;
};
ShortcutManager.prototype.help = function () {
+ var that = this;
var help = [];
var ftree = flatten_shorttree(this._shortcuts);
- for (var shortcut in ftree) {
- var action = this.actions.get(ftree[shortcut]);
+ _.forEach(ftree, function(value, key) {
+ var action = that.actions.get(value);
var help_string = action.help||'== no help ==';
var help_index = action.help_index;
if (help_string) {
- var shortstring = (action.shortstring||shortcut);
+ var shortstring = (action.shortstring||key);
help.push({
shortcut: shortstring,
help: help_string,
help_index: help_index}
);
}
- }
+ });
help.sort(function (a, b) {
if (a.help_index === b.help_index) {
if (a.shortcut === b.shortcut) {
@@ -366,7 +364,7 @@ define([
if(current_node === undefined){
return true;
} else {
- if (typeof(current_node) == 'string'){
+ if (typeof(current_node) === 'string'){
return false;
} else { // assume is a sub-shortcut tree
return this._is_available_shortcut(shortcut_array.slice(1), current_node);
@@ -404,7 +402,6 @@ define([
shortcut = shortcut.toLowerCase();
this.add_shortcut(shortcut, data);
var patch = {keys:{}};
- var b = {bind:{}};
patch.keys[this._mode] = {bind:{}};
patch.keys[this._mode].bind[shortcut] = data;
this._config.update(patch);
@@ -418,7 +415,6 @@ define([
shortcut = shortcut.toLowerCase();
this.remove_shortcut(shortcut);
var patch = {keys: {}};
- var b = {bind: {}};
patch.keys[this._mode] = {bind:{}};
patch.keys[this._mode].bind[shortcut] = null;
this._config.update(patch);
@@ -454,7 +450,14 @@ define([
**/
var action_name = this.actions.get_name(data);
if (! action_name){
- throw new Error('does not know how to deal with : ' + data);
+ if (typeof data === 'string') {
+ // If we have an action name, allow it to be bound anyway.
+ console.log("Unknown action '" + data + "' for shortcut " + shortcut
+ + "; it may be defined by an extension which is not yet loaded.");
+ action_name = data;
+ } else {
+ throw new Error('does not know how to deal with : ' + data);
+ }
}
var _shortcut = normalize_shortcut(shortcut);
this.set_shortcut(_shortcut, action_name);
@@ -471,9 +474,10 @@ define([
*
* data : Dict of the form {key:value, ...}
**/
- for (var shortcut in data) {
- this.add_shortcut(shortcut, data[shortcut], true);
- }
+ var that = this;
+ _.forEach(data, function(value, key) {
+ that.add_shortcut(key, value, true);
+ });
// update the keyboard shortcuts notebook help
this.events.trigger('rebuild.QuickHelp');
};
@@ -560,7 +564,7 @@ define([
return (typeof(action_name) !== 'undefined');
};
- var keyboard = {
+ return {
keycodes : keycodes,
inv_keycodes : inv_keycodes,
ShortcutManager : ShortcutManager,
@@ -569,6 +573,4 @@ define([
shortcut_to_event : shortcut_to_event,
event_to_shortcut : event_to_shortcut,
};
-
- return keyboard;
});
diff --git a/notebook/static/notebook/js/notebook.js b/notebook/static/notebook/js/notebook.js
index 642587545..a9ec36cac 100644
--- a/notebook/static/notebook/js/notebook.js
+++ b/notebook/static/notebook/js/notebook.js
@@ -2761,6 +2761,8 @@ define([
if (that._changed_on_disk_dialog !== null) {
// update save callback on the confirmation button
that._changed_on_disk_dialog.find('.save-confirm-btn').click(_save);
+ //Rebind Click Event on Reload
+ that._changed_on_disk_dialog.find('.btn-warning').click(function () {window.location.reload()});
// redisplay existing dialog
that._changed_on_disk_dialog.modal('show');
} else {
diff --git a/notebook/static/notebook/js/outputarea.js b/notebook/static/notebook/js/outputarea.js
index 2ea1bba88..99bdf97cc 100644
--- a/notebook/static/notebook/js/outputarea.js
+++ b/notebook/static/notebook/js/outputarea.js
@@ -631,6 +631,10 @@ define([
(json.data[MIME_MARKDOWN] !== undefined)) {
this.typeset();
}
+ this.events.trigger('output_updated.OutputArea', {
+ output: json,
+ output_area: this,
+ });
};
OutputArea.prototype._record_display_id = function (json, element) {
diff --git a/notebook/tests/notebook/dualmode_cellinsert.js b/notebook/tests/notebook/dualmode_cellinsert.js
deleted file mode 100644
index f066b16cc..000000000
--- a/notebook/tests/notebook/dualmode_cellinsert.js
+++ /dev/null
@@ -1,82 +0,0 @@
-
-// Test
-casper.notebook_test(function () {
- var a = 'print("a")';
- var index = this.append_cell(a);
- this.execute_cell_then(index);
-
- var b = 'print("b")';
- index = this.append_cell(b);
- this.execute_cell_then(index);
-
- var c = 'print("c")';
- index = this.append_cell(c);
- this.execute_cell_then(index);
-
- this.thenEvaluate(function() {
- IPython.notebook.default_cell_type = 'code';
- });
-
- this.then(function () {
- // Cell insertion
- this.select_cell(2);
- this.trigger_keydown('m'); // Make it markdown
- this.trigger_keydown('a'); // Creates one cell
- this.test.assertEquals(this.get_cell_text(2), '', 'a; New cell 2 text is empty');
- this.test.assertEquals(this.get_cell(2).cell_type, 'code', 'a; inserts a code cell');
- this.validate_notebook_state('a', 'command', 2);
- this.trigger_keydown('b'); // Creates one cell
- this.test.assertEquals(this.get_cell_text(2), '', 'b; Cell 2 text is still empty');
- this.test.assertEquals(this.get_cell_text(3), '', 'b; New cell 3 text is empty');
- this.test.assertEquals(this.get_cell(3).cell_type, 'code', 'b; inserts a code cell');
- this.validate_notebook_state('b', 'command', 3);
- });
-
- this.thenEvaluate(function() {
- IPython.notebook.class_config.set('default_cell_type', 'selected');
- });
-
- this.then(function () {
- this.select_cell(2);
- this.trigger_keydown('m'); // switch it to markdown for the next test
- this.test.assertEquals(this.get_cell(2).cell_type, 'markdown', 'test cell is markdown');
- this.trigger_keydown('a'); // new cell above
- this.test.assertEquals(this.get_cell(2).cell_type, 'markdown', 'a; inserts a markdown cell when markdown selected');
- this.trigger_keydown('b'); // new cell below
- this.test.assertEquals(this.get_cell(3).cell_type, 'markdown', 'b; inserts a markdown cell when markdown selected');
- });
-
- this.thenEvaluate(function() {
- IPython.notebook.class_config.set('default_cell_type', 'above');
- });
-
- this.then(function () {
- this.select_cell(2);
- this.trigger_keydown('y'); // switch it to code for the next test
- this.test.assertEquals(this.get_cell(2).cell_type, 'code', 'test cell is code');
- this.trigger_keydown('b'); // new cell below
- this.test.assertEquals(this.get_cell(3).cell_type, 'code', 'b; inserts a code cell below code cell');
- this.trigger_keydown('a'); // new cell above
- this.test.assertEquals(this.get_cell(3).cell_type, 'code', 'a; inserts a code cell above code cell');
- });
-
- this.then(function () {
- this.set_cell_text(1, 'cell1');
- this.select_cell(1);
- this.select_cell(2, false);
- this.trigger_keydown('a');
- this.test.assertEquals(this.get_cell_text(1), '', 'a; New cell 1 text is empty');
- this.test.assertEquals(this.get_cell_text(2), 'cell1', 'a; Cell 2 text is old cell 1');
-
- this.set_cell_text(1, 'cell1');
- this.set_cell_text(2, 'cell2');
- this.set_cell_text(3, 'cell3');
- this.select_cell(1);
- this.select_cell(2, false);
- this.trigger_keydown('b');
- this.test.assertEquals(this.get_cell_text(1), 'cell1', 'b; Cell 1 remains');
- this.test.assertEquals(this.get_cell_text(2), 'cell2', 'b; Cell 2 remains');
- this.test.assertEquals(this.get_cell_text(3), '', 'b; Cell 3 is new');
- this.test.assertEquals(this.get_cell_text(4), 'cell3', 'b; Cell 4 text is old cell 3');
- });
-});
diff --git a/notebook/tests/selenium/test_deletecell.py b/notebook/tests/selenium/test_deletecell.py
index 388dd2ad8..7c4eebada 100644
--- a/notebook/tests/selenium/test_deletecell.py
+++ b/notebook/tests/selenium/test_deletecell.py
@@ -1,5 +1,3 @@
-import os
-import pytest
def cell_is_deletable(nb, index):
JS = 'return Jupyter.notebook.get_cell({}).is_deletable();'.format(index)
diff --git a/notebook/tests/selenium/test_dualmode_insertcell.py b/notebook/tests/selenium/test_dualmode_insertcell.py
new file mode 100644
index 000000000..4ab100ced
--- /dev/null
+++ b/notebook/tests/selenium/test_dualmode_insertcell.py
@@ -0,0 +1,57 @@
+from selenium.webdriver.common.keys import Keys
+from .utils import shift
+
+def test_insert_cell(notebook):
+ a = "print('a')"
+ b = "print('b')"
+ c = "print('c')"
+
+ notebook.edit_cell(index=0, content=a)
+ notebook.append(b, c)
+ notebook.to_command_mode()
+
+ assert notebook.get_cells_contents() == [a, b, c]
+
+ notebook.to_command_mode()
+ notebook.focus_cell(2)
+ notebook.convert_cell_type(2, "markdown")
+
+ # insert code cell above
+ notebook.current_cell.send_keys("a")
+ assert notebook.get_cell_contents(2) == ""
+ assert notebook.get_cell_type(2) == "code"
+ assert len(notebook.cells) == 4
+
+ # insert code cell below
+ notebook.current_cell.send_keys("b")
+ assert notebook.get_cell_contents(2) == ""
+ assert notebook.get_cell_contents(3) == ""
+ assert notebook.get_cell_type(3) == "code"
+ assert len(notebook.cells) == 5
+
+ notebook.edit_cell(index=1, content="cell1")
+ notebook.focus_cell(1)
+ notebook.current_cell.send_keys("a")
+ assert notebook.get_cell_contents(1) == ""
+ assert notebook.get_cell_contents(2) == "cell1"
+
+ notebook.edit_cell(index=1, content='cell1')
+ notebook.edit_cell(index=2, content='cell2')
+ notebook.edit_cell(index=3, content='cell3')
+ notebook.focus_cell(2)
+ notebook.current_cell.send_keys("b")
+ assert notebook.get_cell_contents(1) == "cell1"
+ assert notebook.get_cell_contents(2) == "cell2"
+ assert notebook.get_cell_contents(3) == ""
+ assert notebook.get_cell_contents(4) == "cell3"
+
+ # insert above multiple selected cells
+ notebook.focus_cell(1)
+ shift(notebook.browser, Keys.DOWN)
+ notebook.current_cell.send_keys('a')
+
+ # insert below multiple selected cells
+ notebook.focus_cell(2)
+ shift(notebook.browser, Keys.DOWN)
+ notebook.current_cell.send_keys('b')
+ assert notebook.get_cells_contents()[1:5] == ["", "cell1", "cell2", ""]
diff --git a/notebook/tests/selenium/utils.py b/notebook/tests/selenium/utils.py
index d17ac0e5e..3645354e0 100644
--- a/notebook/tests/selenium/utils.py
+++ b/notebook/tests/selenium/utils.py
@@ -131,10 +131,17 @@ class Notebook:
JS = 'return Jupyter.notebook.get_cells().map(function(c) {return c.get_text();})'
return self.browser.execute_script(JS)
+ def get_cell_contents(self, index=0, selector='div .CodeMirror-code'):
+ return self.cells[index].find_element_by_css_selector(selector).text
+
def set_cell_metadata(self, index, key, value):
JS = 'Jupyter.notebook.get_cell({}).metadata.{} = {}'.format(index, key, value)
return self.browser.execute_script(JS)
+ def get_cell_type(self, index=0):
+ JS = 'return Jupyter.notebook.get_cell({}).cell_type'.format(index)
+ return self.browser.execute_script(JS)
+
def set_cell_input_prompt(self, index, prmpt_val):
JS = 'Jupyter.notebook.get_cell({}).set_input_prompt({})'.format(index, prmpt_val)
self.browser.execute_script(JS)
diff --git a/notebook/tests/test_config_manager.py b/notebook/tests/test_config_manager.py
index 04ea9c443..ded2d33b9 100644
--- a/notebook/tests/test_config_manager.py
+++ b/notebook/tests/test_config_manager.py
@@ -9,20 +9,27 @@ from notebook.config_manager import BaseJSONConfigManager
def test_json():
tmpdir = tempfile.mkdtemp()
try:
+ root_data = dict(a=1, x=2, nest={'a':1, 'x':2})
with open(os.path.join(tmpdir, 'foo.json'), 'w') as f:
- json.dump(dict(a=1), f)
+ json.dump(root_data, f)
# also make a foo.d/ directory with multiple json files
os.makedirs(os.path.join(tmpdir, 'foo.d'))
with open(os.path.join(tmpdir, 'foo.d', 'a.json'), 'w') as f:
- json.dump(dict(a=2, b=1), f)
+ json.dump(dict(a=2, b=1, nest={'a':2, 'b':1}), f)
with open(os.path.join(tmpdir, 'foo.d', 'b.json'), 'w') as f:
- json.dump(dict(a=3, b=2, c=3), f)
+ json.dump(dict(a=3, b=2, c=3, nest={'a':3, 'b':2, 'c':3}, only_in_b={'x':1}), f)
manager = BaseJSONConfigManager(config_dir=tmpdir, read_directory=False)
data = manager.get('foo')
assert 'a' in data
+ assert 'x' in data
assert 'b' not in data
assert 'c' not in data
assert data['a'] == 1
+ assert 'x' in data['nest']
+ # if we write it out, it also shouldn't pick up the subdirectoy
+ manager.set('foo', data)
+ data = manager.get('foo')
+ assert data == root_data
manager = BaseJSONConfigManager(config_dir=tmpdir, read_directory=True)
data = manager.get('foo')
@@ -33,6 +40,17 @@ def test_json():
assert data['a'] == 1
assert data['b'] == 2
assert data['c'] == 3
+ assert data['nest']['a'] == 1
+ assert data['nest']['b'] == 2
+ assert data['nest']['c'] == 3
+ assert data['nest']['x'] == 2
+
+ # when writing out, we don't want foo.d/*.json data to be included in the root foo.json
+ manager.set('foo', data)
+ manager = BaseJSONConfigManager(config_dir=tmpdir, read_directory=False)
+ data = manager.get('foo')
+ assert data == root_data
+
finally:
shutil.rmtree(tmpdir)
diff --git a/setup.py b/setup.py
index a29cf7959..a94758f7f 100755
--- a/setup.py
+++ b/setup.py
@@ -79,6 +79,9 @@ for more information.
install_requires = [
'jinja2',
'tornado>=4',
+ # pyzmq>=17 is not technically necessary,
+ # but hopefully avoids incompatibilities with Tornado 5. April 2018
+ 'pyzmq>=17',
'ipython_genutils',
'traitlets>=4.2.1',
'jupyter_core>=4.4.0',