Cleanup previous files

Jeremy Tuloup 4 years ago
parent c0f3dc02a0
commit 5b6ed99aec

@ -1,3 +0,0 @@
{
"presets": ["es2015"],
}

@ -1,3 +0,0 @@
{
"directory": "notebook/static/components"
}

@ -1,11 +0,0 @@
name: Enforce PR label
on:
pull_request:
types: [labeled, unlabeled, opened, edited, synchronize]
jobs:
enforce-label:
runs-on: ubuntu-latest
steps:
- name: enforce-triage-label
uses: jupyterlab/maintainer-tools/.github/actions/enforce-label@v1

@ -1,61 +0,0 @@
name: Linux JS Tests
on:
push:
branches: '*'
pull_request:
branches: '*'
jobs:
build:
runs-on: ${{ matrix.os }}-latest
strategy:
fail-fast: false
matrix:
os: [ubuntu, macos]
group: [notebook, base, services]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: 3.8
- name: Set up Node
uses: actions/setup-node@v1
with:
node-version: '12.x'
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Cache pip on Linux
uses: actions/cache@v1
if: startsWith(runner.os, 'Linux')
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ matrix.python }}-${{ hashFiles('**/requirements.txt', 'setup.py') }}
restore-keys: |
${{ runner.os }}-pip-${{ matrix.python }}
- name: Install dependencies
run: |
pip install --upgrade pip
pip install --upgrade setuptools wheel
npm install
npm install -g casperjs@1.1.3 phantomjs-prebuilt@2.1.7
pip install .[test]
- name: Run Tests
run: |
python -m notebook.jstest ${{ matrix.group }}

@ -1,53 +0,0 @@
# The NBConvert Service requires pandoc. Instead of testing
# Pandoc on every operating system (which should already be
# done in nbconvert directly), we'll only test these services
# on ubuntu where we can easily load Pandoc from a Github
# Actions docker image (this docker image is not on other
# operating systems).
name: NBConvert Service Tests
on:
push:
branches: '*'
pull_request:
branches: '*'
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: [ '3.7', '3.8', '3.9', '3.10' ]
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Install Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
architecture: 'x64'
- name: Setup Pandoc
uses: r-lib/actions/setup-pandoc@v1
- name: Upgrade packaging dependencies
run: |
pip install --upgrade pip setuptools wheel
- name: Get pip cache dir
id: pip-cache
run: |
echo "::set-output name=dir::$(pip cache dir)"
- name: Cache pip
uses: actions/cache@v1
with:
path: ${{ steps.pip-cache.outputs.dir }}
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('setup.py') }}
restore-keys: |
${{ runner.os }}-pip-${{ matrix.python-version }}-
${{ runner.os }}-pip-
- name: Install the Python dependencies
run: |
pip install -e .[test]
- name: Run NBConvert Tests
run: |
pytest notebook/nbconvert/tests/
- name: Run NBConvert Service Tests
run: |
pytest notebook/services/nbconvert/tests/

@ -1,53 +0,0 @@
name: Python Tests
on:
push:
branches: '*'
pull_request:
branches: '*'
jobs:
build:
runs-on: ${{ matrix.os }}-latest
strategy:
fail-fast: false
matrix:
os: [ubuntu, macos, windows]
python-version: [ '3.7', '3.8', '3.9', '3.10' ]
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Install Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
architecture: 'x64'
- name: Upgrade packaging dependencies
run: |
pip install --upgrade pip setuptools wheel --user
- name: Get pip cache dir
id: pip-cache
run: |
echo "::set-output name=dir::$(pip cache dir)"
- name: Cache pip
uses: actions/cache@v1
with:
path: ${{ steps.pip-cache.outputs.dir }}
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('setup.py') }}
restore-keys: |
${{ runner.os }}-pip-${{ matrix.python-version }}-
${{ runner.os }}-pip-
- name: Install the Python dependencies
run: |
pip install -e .[test] codecov
- name: List installed packages
run: |
pip freeze
pip check
- name: Run Server-side tests
run: |
pytest -vv --cov notebook --cov-branch --cov-report term-missing:skip-covered --ignore-glob=notebook/tests/selenium/* --ignore-glob=notebook/nbconvert/tests/* --ignore-glob=notebook/services/nbconvert/tests/*
- name: Run Integration Tests
run: |
pytest -v notebook/tests/test_notebookapp_integration.py --integration_tests
- name: Coverage
run: |
codecov

@ -1,46 +0,0 @@
name: Selenium Tests
on:
push:
branches: '*'
pull_request:
branches: '*'
jobs:
build:
runs-on: ${{ matrix.os }}-latest
strategy:
fail-fast: false
matrix:
os: [ubuntu, macos]
python-version: [ '3.7', '3.8', '3.9', '3.10' ]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
architecture: 'x64'
- name: Set up Node
uses: actions/setup-node@v1
with:
node-version: '12.x'
- name: Install JS
run: |
npm install
- name: Install Python dependencies
run: |
python -m pip install -U pip setuptools wheel
pip install --upgrade selenium
pip install pytest
pip install .[test]
- name: Run Tests
run: |
export JUPYTER_TEST_BROWSER=firefox
export MOZ_HEADLESS=1
pytest -sv notebook/tests/selenium

@ -1,149 +0,0 @@
A. J. Holyoake <a.j.holyoake@gmail.com> ajholyoake <a.j.holyoake@gmail.com>
Aaron Culich <aculich@gmail.com> Aaron Culich <aculich@eecs.berkeley.edu>
Aron Ahmadia <aron@ahmadia.net> ahmadia <aron@ahmadia.net>
Benjamin Ragan-Kelley <benjaminrk@gmail.com> <minrk@Mercury.local>
Benjamin Ragan-Kelley <benjaminrk@gmail.com> Min RK
Benjamin Ragan-Kelley <benjaminrk@gmail.com> MinRK <benjaminrk@gmail.com>
Barry Wark <barrywark@gmail.com> Barry Wark <barrywarkatgmaildotcom>
Ben Edwards <bedwards@cs.unm.edu> Ben Edwards <bedwards@sausage.(none)>
Bradley M. Froehle <brad.froehle@gmail.com> Bradley M. Froehle <bfroehle@math.berkeley.edu>
Bradley M. Froehle <brad.froehle@gmail.com> Bradley Froehle <brad.froehle@gmail.com>
Brandon Parsons <brandon@parsonstx.com> Brandon Parsons <brandon.parsons@hp.com>
Brian E. Granger <ellisonbg@gmail.com> Brian Granger
Brian E. Granger <ellisonbg@gmail.com> Brian Granger <>
Brian E. Granger <ellisonbg@gmail.com> bgranger <>
Brian E. Granger <ellisonbg@gmail.com> bgranger <bgranger@red>
Christoph Gohlke <cgohlke@uci.edu> cgohlke <cgohlke@uci.edu>
Cyrille Rossant <cyrille.rossant@gmail.com> rossant <rossant@github>
Damián Avila <damianavila82@yahoo.com.ar> damianavila <damianavila82@yahoo.com.ar>
Damián Avila <damianavila82@yahoo.com.ar> damianavila <damianavila@gmail.com>
Damon Allen <damontallen@gmail.com> damontallen <damontallen@gmail.com>
Darren Dale <dsdale24@gmail.com> darren.dale <>
Darren Dale <dsdale24@gmail.com> Darren Dale <>
Dav Clark <davclark@berkeley.edu> Dav Clark <>
Dav Clark <davclark@berkeley.edu> Dav Clark <davclark@gmail.com>
David Hirschfeld <david.hirschfeld@gazprom-mt.com> dhirschfeld <david.hirschfeld@gazprom-mt.com>
David P. Sanders <dpsanders@gmail.com> David P. Sanders <dpsanders@ciencias.unam.mx>
David Warde-Farley <wardefar@iro.umontreal.ca> David Warde-Farley <>
Doug Blank <dblank@cs.brynmawr.edu> Doug Blank <doug.blank@gmail.com>
Eugene Van den Bulke <eugene.van-den-bulke@gmail.com> Eugene Van den Bulke <eugene.vandenbulke@gmail.com>
Evan Patterson <epatters@enthought.com> <epatters@EPattersons-MacBook-Pro.local>
Evan Patterson <epatters@enthought.com> <epatters@evan-laptop.localdomain>
Evan Patterson <epatters@enthought.com> <epatters@caltech.edu>
Evan Patterson <epatters@enthought.com> <ejpatters@gmail.com>
Evan Patterson <epatters@enthought.com> epatters <ejpatters@gmail.com>
Evan Patterson <epatters@enthought.com> epatters <epatters@enthought.com>
Ernie French <ernestfrench@gmail.com> Ernie French <ernie@gqpbj.com>
Ernie French <ernestfrench@gmail.com> ernie french <ernestfrench@gmail.com>
Ernie French <ernestfrench@gmail.com> ernop <ernestfrench@gmail.com>
Fernando Perez <Fernando.Perez@berkeley.edu> <fperez.net@gmail.com>
Fernando Perez <Fernando.Perez@berkeley.edu> Fernando Perez <fernando.perez@berkeley.edu>
Fernando Perez <Fernando.Perez@berkeley.edu> fperez <>
Fernando Perez <Fernando.Perez@berkeley.edu> fptest <>
Fernando Perez <Fernando.Perez@berkeley.edu> fptest1 <>
Fernando Perez <Fernando.Perez@berkeley.edu> Fernando Perez <fernando.perez@berkeley.edu>
Fernando Perez <fernando.perez@berkeley.edu> Fernando Perez <>
Fernando Perez <fernando.perez@berkeley.edu> Fernando Perez <fperez@maqroll>
Frank Murphy <fpmurphy@mtu.edu> Frank Murphy <fmurphy@arbor.net>
Gabriel Becker <gmbecker@ucdavis.edu> gmbecker <gmbecker@ucdavis.edu>
Gael Varoquaux <gael.varoquaux@normalesup.org> gael.varoquaux <>
Gael Varoquaux <gael.varoquaux@normalesup.org> gvaroquaux <gvaroquaux@gvaroquaux-desktop>
Gael Varoquaux <gael.varoquaux@normalesup.org> Gael Varoquaux <>
Ingolf Becker <ingolf.becker@googlemail.com> watercrossing <ingolf.becker@googlemail.com>
Jake Vanderplas <jakevdp@gmail.com> Jake Vanderplas <vanderplas@astro.washington.edu>
Jakob Gager <jakob.gager@gmail.com> jakobgager <jakob.gager@gmail.com>
Jakob Gager <jakob.gager@gmail.com> jakobgager <gager@ilsb.tuwien.ac.at>
Jakob Gager <jakob.gager@gmail.com> jakobgager <jakobgager@hotmail.com>
Jason Grout <jgrout6@bloomberg.net> <jason.grout@drake.edu>
Jason Grout <jgrout6@bloomberg.net> <jason-github@creativetrax.com>
Jason Gors <jason.gors.work@gmail.com> jason gors <jason.gors.work@gmail.com>
Jason Gors <jason.gors.work@gmail.com> jgors <jason.gors.work@gmail.com>
Jens Hedegaard Nielsen <jenshnielsen@gmail.com> Jens Hedegaard Nielsen <jhn@jhn-Znote.(none)>
Jens Hedegaard Nielsen <jenshnielsen@gmail.com> Jens H Nielsen <jenshnielsen@gmail.com>
Jens Hedegaard Nielsen <jenshnielsen@gmail.com> Jens H. Nielsen <jenshnielsen@gmail.com>
Jez Ng <jezreel@gmail.com> Jez Ng <me@jezng.com>
Jonathan Frederic <jdfreder@calpoly.edu> Jonathan Frederic <jonathan@LifebookMint.(none)>
Jonathan Frederic <jdfreder@calpoly.edu> Jonathan Frederic <jon.freder@gmail.com>
Jonathan Frederic <jdfreder@calpoly.edu> Jonathan Frederic <xh3xx.goose@gmail.com>
Jonathan Frederic <jdfreder@calpoly.edu> jon <jon.freder@gmail.com>
Jonathan Frederic <jdfreder@calpoly.edu> U-Jon-PC\Jon <Jon@Jon-PC.(none)>
Jonathan March <jmarch@enthought.com> Jonathan March <JDM@MarchRay.net>
Jonathan March <jmarch@enthought.com> jdmarch <JDM@marchRay.net>
Jörgen Stenarson <jorgen.stenarson@kroywen.se> Jörgen Stenarson <jorgen.stenarson@bostream.nu>
Jörgen Stenarson <jorgen.stenarson@kroywen.se> Jorgen Stenarson <jorgen.stenarson@bostream.nu>
Jörgen Stenarson <jorgen.stenarson@kroywen.se> Jorgen Stenarson <>
Jörgen Stenarson <jorgen.stenarson@kroywen.se> jstenar <jorgen.stenarson@bostream.nu>
Jörgen Stenarson <jorgen.stenarson@kroywen.se> jstenar <>
Jörgen Stenarson <jorgen.stenarson@kroywen.se> Jörgen Stenarson <jorgen.stenarson@kroywen.se>
Juergen Hasch <python@elbonia.de> juhasch <python@elbonia.de>
Juergen Hasch <python@elbonia.de> juhasch <hasch@VMBOX.fritz.box>
Julia Evans <julia@jvns.ca> Julia Evans <julia@stripe.com>
Kester Tong <kestert@google.com> KesterTong <kestert@google.com>
Kyle Kelley <rgbkrk@gmail.com> Kyle Kelley <kyle.kelley@rackspace.com>
Kyle Kelley <rgbkrk@gmail.com> rgbkrk <rgbkrk@gmail.com>
Laurent Dufréchou <laurent.dufrechou@gmail.com> <laurent.dufrechou@gmail.com>
Laurent Dufréchou <laurent.dufrechou@gmail.com> <laurent@Pep>
Laurent Dufréchou <laurent.dufrechou@gmail.com> laurent dufrechou <>
Laurent Dufréchou <laurent.dufrechou@gmail.com> laurent.dufrechou <>
Laurent Dufréchou <laurent.dufrechou@gmail.com> Laurent Dufrechou <>
Laurent Dufréchou <laurent.dufrechou@gmail.com> laurent.dufrechou@gmail.com <>
Laurent Dufréchou <laurent.dufrechou@gmail.com> ldufrechou <ldufrechou@PEP>
Lorena Pantano <lorena.pantano@gmail.com> Lorena <lorena.pantano@gmail.com>
Luis Pedro Coelho <luis@luispedro.org> Luis Pedro Coelho <lpc@cmu.edu>
Marc Molla <marcmolla@gmail.com> marcmolla <marcmolla@gmail.com>
Martín Gaitán <gaitan@gmail.com> Martín Gaitán <gaitan@phasety.com>
Matthias Bussonnier <bussonniermatthias@gmail.com> Matthias BUSSONNIER <bussonniermatthias@gmail.com>
Matthias Bussonnier <bussonniermatthias@gmail.com> Bussonnier Matthias <bussonniermatthias@gmail.com>
Matthias Bussonnier <bussonniermatthias@gmail.com> Matthias BUSSONNIER <bussonniermatthias@umr168-curn-1-24x-6561.curie.fr>
Matthias Bussonnier <bussonniermatthias@gmail.com> Matthias Bussonnier <carreau@Aspire.(none)>
Michael Droettboom <mdboom@gmail.com> Michael Droettboom <mdroe@stsci.edu>
Nicholas Bollweg <nick.bollweg@gmail.com> Nicholas Bollweg (Nick) <nick.bollweg@gmail.com>
Nicolas Rougier <Nicolas.Rougier@inria.fr> <Nicolas.rougier@inria.fr>
Nikolay Koldunov <koldunovn@gmail.com> Nikolay Koldunov <nikolay.koldunov@zmaw.de>
Omar Andrés Zapata Mesa <andresete.chaos@gmail.com> Omar Andres Zapata Mesa <andresete.chaos@gmail.com>
Omar Andrés Zapata Mesa <andresete.chaos@gmail.com> Omar Andres Zapata Mesa <omazapa@tuxhome>
Pankaj Pandey <pankaj86@gmail.com> Pankaj Pandey <pankaj@enthought.com>
Pascal Schetelat <pascal.schetelat@gmail.com> pascal-schetelat <pascal.schetelat@gmail.com>
Paul Ivanov <pi@berkeley.edu> Paul Ivanov <pivanov314@gmail.com>
Pauli Virtanen <pauli.virtanen@iki.fi> Pauli Virtanen <>
Pauli Virtanen <pauli.virtanen@iki.fi> Pauli Virtanen <pav@iki.fi>
Pierre Gerold <pierre.gerold@laposte.net> Pierre Gerold <gerold@crans.org>
Pietro Berkes <pberkes@enthought.com> Pietro Berkes <pietro.berkes@googlemail.com>
Piti Ongmongkolkul <piti118@gmail.com> piti118 <piti118@gmail.com>
Prabhu Ramachandran <prabhu@enthought.com> Prabhu Ramachandran <>
Puneeth Chaganti <punchagan@gmail.com> Puneeth Chaganti <punchagan@muse-amuse.in>
Robert Kern <robert.kern@gmail.com> rkern <>
Robert Kern <robert.kern@gmail.com> Robert Kern <rkern@enthought.com>
Robert Kern <robert.kern@gmail.com> Robert Kern <rkern@Sacrilege.local>
Robert Kern <robert.kern@gmail.com> Robert Kern <>
Robert Marchman <bo.marchman@gmail.com> Robert Marchman <robert.l.marchman@dartmouth.edu>
Satrajit Ghosh <satra@mit.edu> Satrajit Ghosh <satra@ba5.mit.edu>
Satrajit Ghosh <satra@mit.edu> Satrajit Ghosh <satrajit.ghosh@gmail.com>
Scott Sanderson <scoutoss@gmail.com> Scott Sanderson <ssanderson@quantopian.com>
smithj1 <smithj1@LMC-022896.local> smithj1 <smithj1@LMC-022896.swisscom.com>
smithj1 <smithj1@LMC-022896.local> smithj1 <smithj1@lmc-022896.local>
Steven Johnson <steven.johnson@drake.edu> stevenJohnson <steven.johnson@drake.edu>
Steven Silvester <steven.silvester@ieee.org> blink1073 <steven.silvester@ieee.org>
S. Weber <s8weber@c4.usr.sh> s8weber <s8weber@c5.usr.sh>
Stefan van der Walt <stefan@sun.ac.za> Stefan van der Walt <bzr@mentat.za.net>
Silvia Vinyes <silvia.vinyes@gmail.com> Silvia <silvia@silvia-U44SG.(none)>
Silvia Vinyes <silvia.vinyes@gmail.com> silviav12 <silvia.vinyes@gmail.com>
Sylvain Corlay <scorlay@bloomberg.net> <sylvain.corlay@gmail.com>
Sylvain Corlay <scorlay@bloomberg.net> sylvain.corlay <sylvain.corlay@gmail.com>
Ted Drain <ted.drain@gmail.com> TD22057 <ted.drain@gmail.com>
Théophile Studer <theo.studer@gmail.com> Théophile Studer <studer@users.noreply.github.com>
Thomas Kluyver <takowl@gmail.com> Thomas <takowl@gmail.com>
Thomas Spura <tomspur@fedoraproject.org> Thomas Spura <thomas.spura@gmail.com>
Timo Paulssen <timonator@perpetuum-immobile.de> timo <timonator@perpetuum-immobile.de>
vds <vds@VIVIAN> vds2212 <vds2212@VIVIAN>
vds <vds@VIVIAN> vds <vds@vivian>
Ville M. Vainio <vivainio@gmail.com> <vivainio2@WN-W0941>
Ville M. Vainio <vivainio@gmail.com> ville <ville@VILLE-PC>
Ville M. Vainio <vivainio@gmail.com> ville <ville@ville-desktop>
Ville M. Vainio <vivainio@gmail.com> vivainio <>
Ville M. Vainio <vivainio@gmail.com> Ville M. Vainio <vivainio@villev>
Ville M. Vainio <vivainio@gmail.com> Ville M. Vainio <vivainio@ville_vmw>
Walter Doerwald <walter@livinglogic.de> walter.doerwald <>
Walter Doerwald <walter@livinglogic.de> Walter Doerwald <>
W. Trevor King <wking@tremily.us> W. Trevor King <wking@drexel.edu>
Yoval P. <yoval@gmx.com> y-p <yoval@gmx.com>

@ -1,197 +0,0 @@
Contributing to the Jupyter Notebook
====================================
If you're reading this section, you're probably interested in contributing to
Jupyter. Welcome and thanks for your interest in contributing!
Please take a look at the Contributor documentation, familiarize yourself with
using the Jupyter Notebook, and introduce yourself on the mailing list and
share what area of the project you are interested in working on.
General Guidelines
------------------
For general documentation about contributing to Jupyter projects, see the
`Project Jupyter Contributor Documentation`__.
__ https://jupyter.readthedocs.io/en/latest/contributing/content-contributor.html
Setting Up a Development Environment
------------------------------------
Installing Node.js and npm
^^^^^^^^^^^^^^^^^^^^^^^^^^
Building the Notebook from its GitHub source code requires some tools to
create and minify JavaScript components and the CSS,
specifically Node.js and Node's package manager, ``npm``.
It should be node version ≥ 6.0.
If you use ``conda``, you can get them with::
conda install -c conda-forge nodejs
If you use `Homebrew <https://brew.sh/>`_ on Mac OS X::
brew install node
Installation on Linux may vary, but be aware that the `nodejs` or `npm` packages
included in the system package repository may be too old to work properly.
You can also use the installer from the `Node.js website <https://nodejs.org>`_.
Installing the Jupyter Notebook
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Once you have installed the dependencies mentioned above, use the following
steps::
pip install --upgrade setuptools pip
git clone https://github.com/jupyter/notebook
cd notebook
pip install -e .
If you are using a system-wide Python installation and you only want to install the notebook for you,
you can add ``--user`` to the install commands.
Once you have done this, you can launch the master branch of Jupyter notebook
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". 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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
There is a build step for the JavaScript and CSS in the notebook.
To make sure that you are working with up-to-date code, you will need to run
this command whenever there are changes to JavaScript or LESS sources::
npm run build
**IMPORTANT:** Don't forget to run ``npm run build`` after switching branches.
When switching between branches of different versions (e.g. ``4.x`` and
``master``), run ``pip install -e .``. If you have tried the above and still
find that the notebook is not reflecting the current source code, try cleaning
the repo with ``git clean -xfd`` and reinstalling with ``pip install -e .``.
Development Tip
"""""""""""""""
When doing development, you can use this command to automatically rebuild
JavaScript and LESS sources as they are modified::
npm run build:watch
Git Hooks
"""""""""
If you want to automatically update dependencies and recompile JavaScript and
CSS after checking out a new commit, you can install post-checkout and
post-merge hooks which will do it for you::
git-hooks/install-hooks.sh
See ``git-hooks/README.md`` for more details.
Running Tests
-------------
Python Tests
^^^^^^^^^^^^
Install dependencies::
pip install -e '.[test]'
To run the Python tests, use::
pytest
If you want coverage statistics as well, you can run::
py.test --cov notebook -v --pyargs notebook
JavaScript Tests
^^^^^^^^^^^^^^^^
To run the JavaScript tests, you will need to have PhantomJS and CasperJS
installed::
npm install -g casperjs phantomjs-prebuilt
Then, to run the JavaScript tests::
python -m notebook.jstest [group]
where ``[group]`` is an optional argument that is a path relative to
``notebook/tests/``.
For example, to run all tests in ``notebook/tests/notebook``::
python -m notebook.jstest notebook
or to run just ``notebook/tests/notebook/deletecell.js``::
python -m notebook.jstest notebook/deletecell.js
Building the Documentation
--------------------------
To build the documentation you'll need `Sphinx <http://www.sphinx-doc.org/>`_,
`pandoc <http://pandoc.org/>`_ and a few other packages.
To install (and activate) a conda environment named ``notebook_docs``
containing all the necessary packages (except pandoc), use::
conda create -n notebook_docs pip
conda activate notebook_docs # Linux and OS X
activate notebook_docs # Windows
pip install .[docs]
If you want to install the necessary packages with ``pip``, use the following instead::
pip install .[docs]
Once you have installed the required packages, you can build the docs with::
cd docs
make html
After that, the generated HTML files will be available at
``build/html/index.html``. You may view the docs in your browser.
You can automatically check if all hyperlinks are still valid::
make linkcheck
Windows users can find ``make.bat`` in the ``docs`` folder.
You should also have a look at the `Project Jupyter Documentation Guide`__.
__ https://jupyter.readthedocs.io/en/latest/contributing/docs-contributions/index.html

File diff suppressed because one or more lines are too long

@ -1,29 +0,0 @@
{
"name": "jupyter-notebook-deps",
"version": "0.0.1",
"dependencies": {
"backbone": "components/backbone#~1.2",
"bootstrap": "bootstrap#~3.4",
"bootstrap-tour": "0.9.0",
"codemirror": "components/codemirror#5.56.0+components1",
"create-react-class": "https://cdn.jsdelivr.net/npm/create-react-class@15.6.3/create-react-class.min.js",
"es6-promise": "~1.0",
"font-awesome": "components/font-awesome#~4.7.0",
"jed": "~1.1.1",
"jquery": "components/jquery#~3.5.0",
"jquery-typeahead": "~2.10.6",
"jquery-ui": "components/jqueryui#~1.12",
"marked": "~0.7",
"MathJax": "^2.7.4",
"moment": "~2.19.3",
"react": "~16.0.0",
"requirejs": "~2.2",
"requirejs-text": "~2.0.15",
"requirejs-plugins": "~1.0.3",
"text-encoding": "~0.1",
"underscore": "components/underscore#~1.8.3",
"xterm.js": "https://unpkg.com/xterm@~3.1.0/dist/xterm.js",
"xterm.js-css": "https://unpkg.com/xterm@~3.1.0/dist/xterm.css",
"xterm.js-fit": "https://unpkg.com/xterm@~3.1.0/dist/addons/fit/fit.js"
}
}

@ -1,9 +0,0 @@
git hooks for Jupyter
add these to your `.git/hooks`
For now, we just have `post-checkout` and `post-merge`,
both of which attempt to rebuild javascript and css sourcemaps,
so make sure that you have a fully synced repo whenever you checkout or pull.
To use these hooks, run `./install-hooks.sh`.

@ -1,9 +0,0 @@
#!/usr/bin/env bash
DOTGIT=`git rev-parse --git-dir`
TOPLEVEL=`git rev-parse --show-toplevel`
TO=${DOTGIT}/hooks
FROM=${TOPLEVEL}/git-hooks
ln -s ${FROM}/post-checkout ${TO}/post-checkout
ln -s ${FROM}/post-merge ${TO}/post-merge

@ -1,22 +0,0 @@
#!/bin/bash
if [[ "$(basename $0)" == "post-merge" ]]; then
PREVIOUS_HEAD=ORIG_HEAD
else
PREVIOUS_HEAD=$1
fi
# if style changed (and less available), rebuild sourcemaps
if [[
! -z "$(git diff $PREVIOUS_HEAD notebook/static/*/js/**.js)"
]]; then
echo "rebuilding javascript"
python setup.py js || echo "fail to rebuild javascript"
fi
if [[
! -z "$(git diff $PREVIOUS_HEAD notebook/static/*/less/**.less)"
]]; then
echo "rebuilding css sourcemaps"
python setup.py css || echo "fail to recompile css"
fi

@ -1 +0,0 @@
post-checkout

@ -1,11 +0,0 @@
[Desktop Entry]
Name=Jupyter Notebook
Comment=Run Jupyter Notebook
Exec=jupyter-notebook %f
Terminal=true
Type=Application
Icon=notebook
StartupNotify=true
MimeType=application/x-ipynb+json;
Categories=Development;Education;
Keywords=python;

@ -1,96 +0,0 @@
"""
Utilities for getting information about Jupyter and the system it's running in.
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import os
import platform
import pprint
import sys
import subprocess
from ipython_genutils import py3compat, encoding
import notebook
def pkg_commit_hash(pkg_path):
"""Get short form of commit hash given directory `pkg_path`
We get the commit hash from git if it's a repo.
If this fail, we return a not-found placeholder tuple
Parameters
----------
pkg_path : str
directory containing package
only used for getting commit from active repo
Returns
-------
hash_from : str
Where we got the hash from - description
hash_str : str
short form of hash
"""
# maybe we are in a repository, check for a .git folder
p = os.path
cur_path = None
par_path = pkg_path
while cur_path != par_path:
cur_path = par_path
if p.exists(p.join(cur_path, '.git')):
try:
proc = subprocess.Popen(['git', 'rev-parse', '--short', 'HEAD'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=pkg_path)
repo_commit, _ = proc.communicate()
except OSError:
repo_commit = None
if repo_commit:
return 'repository', repo_commit.strip().decode('ascii')
else:
return u'', u''
par_path = p.dirname(par_path)
return u'', u''
def pkg_info(pkg_path):
"""Return dict describing the context of this package
Parameters
----------
pkg_path : str
path containing __init__.py for package
Returns
-------
context : dict
with named parameters of interest
"""
src, hsh = pkg_commit_hash(pkg_path)
return dict(
notebook_version=notebook.__version__,
notebook_path=pkg_path,
commit_source=src,
commit_hash=hsh,
sys_version=sys.version,
sys_executable=sys.executable,
sys_platform=sys.platform,
platform=platform.platform(),
os_name=os.name,
default_encoding=encoding.DEFAULT_ENCODING,
)
def get_sys_info():
"""Return useful information about the system as a dict."""
p = os.path
path = p.realpath(p.dirname(p.abspath(p.join(notebook.__file__))))
return pkg_info(path)

@ -1,41 +0,0 @@
"""
Timezone utilities
Just UTC-awareness right now
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from datetime import tzinfo, timedelta, datetime
# constant for zero offset
ZERO = timedelta(0)
class tzUTC(tzinfo):
"""tzinfo object for UTC (zero offset)"""
def utcoffset(self, d):
return ZERO
def dst(self, d):
return ZERO
UTC = tzUTC()
def utc_aware(unaware):
"""decorator for adding UTC tzinfo to datetime's utcfoo methods"""
def utc_method(*args, **kwargs):
dt = unaware(*args, **kwargs)
return dt.replace(tzinfo=UTC)
return utc_method
utcfromtimestamp = utc_aware(datetime.utcfromtimestamp)
utcnow = utc_aware(datetime.utcnow)
def isoformat(dt):
"""Return iso-formatted timestamp
Like .isoformat(), but uses Z for UTC instead of +00:00
"""
return dt.isoformat().replace('+00:00', 'Z')

@ -1 +0,0 @@
from .security import passwd

@ -1,42 +0,0 @@
from notebook.auth import passwd
from getpass import getpass
from notebook.config_manager import BaseJSONConfigManager
from jupyter_core.paths import jupyter_config_dir
import argparse
import sys
def set_password(args):
password = args.password
while not password :
password1 = getpass("" if args.quiet else "Provide password: ")
password_repeat = getpass("" if args.quiet else "Repeat password: ")
if password1 != password_repeat:
print("Passwords do not match, try again")
elif len(password1) < 4:
print("Please provide at least 4 characters")
else:
password = password1
password_hash = passwd(password)
cfg = BaseJSONConfigManager(config_dir=jupyter_config_dir())
cfg.update('jupyter_notebook_config', {
'NotebookApp': {
'password': password_hash,
}
})
if not args.quiet:
print("password stored in config dir: %s" % jupyter_config_dir())
def main(argv):
parser = argparse.ArgumentParser(argv[0])
subparsers = parser.add_subparsers()
parser_password = subparsers.add_parser('password', help='sets a password for your notebook server')
parser_password.add_argument("password", help="password to set, if not given, a password will be queried for (NOTE: this may not be safe)",
nargs="?")
parser_password.add_argument("--quiet", help="suppress messages", action="store_true")
parser_password.set_defaults(function=set_password)
args = parser.parse_args(argv[1:])
args.function(args)
if __name__ == "__main__":
main(sys.argv)

@ -1,253 +0,0 @@
"""Tornado handlers for logging into the notebook."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import re
import os
from urllib.parse import urlparse
import uuid
from tornado.escape import url_escape
from .security import passwd_check, set_password
from ..base.handlers import IPythonHandler
class LoginHandler(IPythonHandler):
"""The basic tornado login handler
authenticates with a hashed password from the configuration.
"""
def _render(self, message=None):
self.write(self.render_template('login.html',
next=url_escape(self.get_argument('next', default=self.base_url)),
message=message,
))
def _redirect_safe(self, url, default=None):
"""Redirect if url is on our PATH
Full-domain redirects are allowed if they pass our CORS origin checks.
Otherwise use default (self.base_url if unspecified).
"""
if default is None:
default = self.base_url
# protect chrome users from mishandling unescaped backslashes.
# \ is not valid in urls, but some browsers treat it as /
# instead of %5C, causing `\\` to behave as `//`
url = url.replace("\\", "%5C")
parsed = urlparse(url)
if parsed.netloc or not (parsed.path + '/').startswith(self.base_url):
# require that next_url be absolute path within our path
allow = False
# OR pass our cross-origin check
if parsed.netloc:
# if full URL, run our cross-origin check:
origin = '%s://%s' % (parsed.scheme, parsed.netloc)
origin = origin.lower()
if self.allow_origin:
allow = self.allow_origin == origin
elif self.allow_origin_pat:
allow = bool(self.allow_origin_pat.match(origin))
if not allow:
# not allowed, use default
self.log.warning("Not allowing login redirect to %r" % url)
url = default
self.redirect(url)
def get(self):
if self.current_user:
next_url = self.get_argument('next', default=self.base_url)
self._redirect_safe(next_url)
else:
self._render()
@property
def hashed_password(self):
return self.password_from_settings(self.settings)
def passwd_check(self, a, b):
return passwd_check(a, b)
def post(self):
typed_password = self.get_argument('password', default=u'')
new_password = self.get_argument('new_password', default=u'')
if self.get_login_available(self.settings):
if self.passwd_check(self.hashed_password, typed_password) and not new_password:
self.set_login_cookie(self, uuid.uuid4().hex)
elif self.token and self.token == typed_password:
self.set_login_cookie(self, uuid.uuid4().hex)
if new_password and self.settings.get('allow_password_change'):
config_dir = self.settings.get('config_dir')
config_file = os.path.join(config_dir, 'jupyter_notebook_config.json')
set_password(new_password, config_file=config_file)
self.log.info("Wrote hashed password to %s" % config_file)
else:
self.set_status(401)
self._render(message={'error': 'Invalid credentials'})
return
next_url = self.get_argument('next', default=self.base_url)
self._redirect_safe(next_url)
@classmethod
def set_login_cookie(cls, handler, user_id=None):
"""Call this on handlers to set the login cookie for success"""
cookie_options = handler.settings.get('cookie_options', {})
cookie_options.setdefault('httponly', True)
# tornado <4.2 has a bug that considers secure==True as soon as
# 'secure' kwarg is passed to set_secure_cookie
if handler.settings.get('secure_cookie', handler.request.protocol == 'https'):
cookie_options.setdefault('secure', True)
cookie_options.setdefault('path', handler.base_url)
handler.set_secure_cookie(handler.cookie_name, user_id, **cookie_options)
return user_id
auth_header_pat = re.compile(r'token\s+(.+)', re.IGNORECASE)
@classmethod
def get_token(cls, handler):
"""Get the user token from a request
Default:
- in URL parameters: ?token=<token>
- in header: Authorization: token <token>
"""
user_token = handler.get_argument('token', '')
if not user_token:
# get it from Authorization header
m = cls.auth_header_pat.match(handler.request.headers.get('Authorization', ''))
if m:
user_token = m.group(1)
return user_token
@classmethod
def should_check_origin(cls, handler):
"""Should the Handler check for CORS origin validation?
Origin check should be skipped for token-authenticated requests.
Returns:
- True, if Handler must check for valid CORS origin.
- False, if Handler should skip origin check since requests are token-authenticated.
"""
return not cls.is_token_authenticated(handler)
@classmethod
def is_token_authenticated(cls, handler):
"""Returns True if handler has been token authenticated. Otherwise, False.
Login with a token is used to signal certain things, such as:
- permit access to REST API
- xsrf protection
- skip origin-checks for scripts
"""
if getattr(handler, '_user_id', None) is None:
# ensure get_user has been called, so we know if we're token-authenticated
handler.get_current_user()
return getattr(handler, '_token_authenticated', False)
@classmethod
def get_user(cls, handler):
"""Called by handlers.get_current_user for identifying the current user.
See tornado.web.RequestHandler.get_current_user for details.
"""
# Can't call this get_current_user because it will collide when
# called on LoginHandler itself.
if getattr(handler, '_user_id', None):
return handler._user_id
user_id = cls.get_user_token(handler)
if user_id is None:
get_secure_cookie_kwargs = handler.settings.get('get_secure_cookie_kwargs', {})
user_id = handler.get_secure_cookie(handler.cookie_name, **get_secure_cookie_kwargs )
else:
cls.set_login_cookie(handler, user_id)
# Record that the current request has been authenticated with a token.
# Used in is_token_authenticated above.
handler._token_authenticated = True
if user_id is None:
# If an invalid cookie was sent, clear it to prevent unnecessary
# extra warnings. But don't do this on a request with *no* cookie,
# because that can erroneously log you out (see gh-3365)
if handler.get_cookie(handler.cookie_name) is not None:
handler.log.warning("Clearing invalid/expired login cookie %s", handler.cookie_name)
handler.clear_login_cookie()
if not handler.login_available:
# Completely insecure! No authentication at all.
# No need to warn here, though; validate_security will have already done that.
user_id = 'anonymous'
# cache value for future retrievals on the same request
handler._user_id = user_id
return user_id
@classmethod
def get_user_token(cls, handler):
"""Identify the user based on a token in the URL or Authorization header
Returns:
- uuid if authenticated
- None if not
"""
token = handler.token
if not token:
return
# check login token from URL argument or Authorization header
user_token = cls.get_token(handler)
authenticated = False
if user_token == token:
# token-authenticated, set the login cookie
handler.log.debug("Accepting token-authenticated connection from %s", handler.request.remote_ip)
authenticated = True
if authenticated:
return uuid.uuid4().hex
else:
return None
@classmethod
def validate_security(cls, app, ssl_options=None):
"""Check the notebook application's security.
Show messages, or abort if necessary, based on the security configuration.
"""
if not app.ip:
warning = "WARNING: The notebook server is listening on all IP addresses"
if ssl_options is None:
app.log.warning(warning + " and not using encryption. This "
"is not recommended.")
if not app.password and not app.token:
app.log.warning(warning + " and not using authentication. "
"This is highly insecure and not recommended.")
else:
if not app.password and not app.token:
app.log.warning(
"All authentication is disabled."
" Anyone who can connect to this server will be able to run code.")
@classmethod
def password_from_settings(cls, settings):
"""Return the hashed password from the tornado settings.
If there is no configured password, an empty string will be returned.
"""
return settings.get('password', u'')
@classmethod
def get_login_available(cls, settings):
"""Whether this LoginHandler is needed - and therefore whether the login page should be displayed."""
return bool(cls.password_from_settings(settings) or settings.get('token'))

@ -1,23 +0,0 @@
"""Tornado handlers for logging out of the notebook.
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from ..base.handlers import IPythonHandler
class LogoutHandler(IPythonHandler):
def get(self):
self.clear_login_cookie()
if self.login_available:
message = {'info': 'Successfully logged out.'}
else:
message = {'warning': 'Cannot log out. Notebook authentication '
'is disabled.'}
self.write(self.render_template('logout.html',
message=message))
default_handlers = [(r"/logout", LogoutHandler)]

@ -1,172 +0,0 @@
"""
Password generation for the Notebook.
"""
from contextlib import contextmanager
import getpass
import hashlib
import io
import json
import os
import random
import traceback
import warnings
from ipython_genutils.py3compat import cast_bytes, str_to_bytes, cast_unicode
from traitlets.config import Config, ConfigFileNotFound, JSONFileConfigLoader
from jupyter_core.paths import jupyter_config_dir
# Length of the salt in nr of hex chars, which implies salt_len * 4
# bits of randomness.
salt_len = 12
def passwd(passphrase=None, algorithm='argon2'):
"""Generate hashed password and salt for use in notebook configuration.
In the notebook configuration, set `c.NotebookApp.password` to
the generated string.
Parameters
----------
passphrase : str
Password to hash. If unspecified, the user is asked to input
and verify a password.
algorithm : str
Hashing algorithm to use (e.g, 'sha1' or any argument supported
by :func:`hashlib.new`, or 'argon2').
Returns
-------
hashed_passphrase : str
Hashed password, in the format 'hash_algorithm:salt:passphrase_hash'.
Examples
--------
>>> passwd('mypassword', algorithm='sha1')
'sha1:7cf3:b7d6da294ea9592a9480c8f52e63cd42cfb9dd12'
"""
if passphrase is None:
for i in range(3):
p0 = getpass.getpass('Enter password: ')
p1 = getpass.getpass('Verify password: ')
if p0 == p1:
passphrase = p0
break
else:
print('Passwords do not match.')
else:
raise ValueError('No matching passwords found. Giving up.')
if algorithm == 'argon2':
from argon2 import PasswordHasher
ph = PasswordHasher(
memory_cost=10240,
time_cost=10,
parallelism=8,
)
h = ph.hash(passphrase)
return ':'.join((algorithm, cast_unicode(h, 'ascii')))
else:
h = hashlib.new(algorithm)
salt = ('%0' + str(salt_len) + 'x') % random.getrandbits(4 * salt_len)
h.update(cast_bytes(passphrase, 'utf-8') + str_to_bytes(salt, 'ascii'))
return ':'.join((algorithm, salt, h.hexdigest()))
def passwd_check(hashed_passphrase, passphrase):
"""Verify that a given passphrase matches its hashed version.
Parameters
----------
hashed_passphrase : str
Hashed password, in the format returned by `passwd`.
passphrase : str
Passphrase to validate.
Returns
-------
valid : bool
True if the passphrase matches the hash.
Examples
--------
>>> from notebook.auth.security import passwd_check
>>> passwd_check('argon2:...', 'mypassword')
True
>>> passwd_check('argon2:...', 'otherpassword')
False
>>> passwd_check('sha1:0e112c3ddfce:a68df677475c2b47b6e86d0467eec97ac5f4b85a',
... 'mypassword')
True
"""
if hashed_passphrase.startswith('argon2:'):
import argon2
import argon2.exceptions
ph = argon2.PasswordHasher()
try:
return ph.verify(hashed_passphrase[7:], passphrase)
except argon2.exceptions.VerificationError:
return False
else:
try:
algorithm, salt, pw_digest = hashed_passphrase.split(':', 2)
except (ValueError, TypeError):
return False
try:
h = hashlib.new(algorithm)
except ValueError:
return False
if len(pw_digest) == 0:
return False
h.update(cast_bytes(passphrase, 'utf-8') + cast_bytes(salt, 'ascii'))
return h.hexdigest() == pw_digest
@contextmanager
def persist_config(config_file=None, mode=0o600):
"""Context manager that can be used to modify a config object
On exit of the context manager, the config will be written back to disk,
by default with user-only (600) permissions.
"""
if config_file is None:
config_file = os.path.join(jupyter_config_dir(), 'jupyter_notebook_config.json')
os.makedirs(os.path.dirname(config_file), exist_ok=True)
loader = JSONFileConfigLoader(os.path.basename(config_file), os.path.dirname(config_file))
try:
config = loader.load_config()
except ConfigFileNotFound:
config = Config()
yield config
with io.open(config_file, 'w', encoding='utf8') as f:
f.write(cast_unicode(json.dumps(config, indent=2)))
try:
os.chmod(config_file, mode)
except Exception as e:
tb = traceback.format_exc()
warnings.warn("Failed to set permissions on %s:\n%s" % (config_file, tb),
RuntimeWarning)
def set_password(password=None, config_file=None):
"""Ask user for password, store it in notebook json configuration file"""
hashed_password = passwd(password)
with persist_config(config_file) as config:
config.NotebookApp.password = hashed_password

@ -1,48 +0,0 @@
"""Tests for login redirects"""
import requests
from tornado.httputil import url_concat
from notebook.tests.launchnotebook import NotebookTestBase
class LoginTest(NotebookTestBase):
def login(self, next):
first = requests.get(self.base_url() + "login")
first.raise_for_status()
resp = requests.post(
url_concat(
self.base_url() + "login",
{'next': next},
),
allow_redirects=False,
data={
"password": self.token,
"_xsrf": first.cookies.get("_xsrf", ""),
},
cookies=first.cookies,
)
resp.raise_for_status()
return resp.headers['Location']
def test_next_bad(self):
for bad_next in (
"//some-host",
"//host" + self.url_prefix + "tree",
"https://google.com",
"/absolute/not/base_url",
):
url = self.login(next=bad_next)
self.assertEqual(url, self.url_prefix)
assert url
def test_next_ok(self):
for next_path in (
"tree/",
"//" + self.url_prefix + "tree",
"notebooks/notebook.ipynb",
"tree//something",
):
expected = self.url_prefix + next_path
actual = self.login(next=expected)
self.assertEqual(actual, expected)

@ -1,25 +0,0 @@
from ..security import passwd, passwd_check
def test_passwd_structure():
p = passwd('passphrase')
algorithm, hashed = p.split(':')
assert algorithm == 'argon2'
assert hashed.startswith('$argon2id$')
def test_roundtrip():
p = passwd('passphrase')
assert passwd_check(p, 'passphrase') == True
def test_bad():
p = passwd('passphrase')
assert passwd_check(p, p) == False
assert passwd_check(p, 'a:b:c:d') == False
assert passwd_check(p, 'a:b') == False
def test_passwd_check_unicode():
# GH issue #4524
phash = u'sha1:23862bc21dd3:7a415a95ae4580582e314072143d9c382c491e4f'
assert passwd_check(phash, u"łe¶ŧ←↓→")
phash = (u'argon2:$argon2id$v=19$m=10240,t=10,p=8$'
u'qjjDiZUofUVVnrVYxacnbA$l5pQq1bJ8zglGT2uXP6iOg')
assert passwd_check(phash, u"łe¶ŧ←↓→")

@ -1,952 +0,0 @@
"""Base Tornado handlers for the notebook server."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import datetime
import functools
import ipaddress
import json
import mimetypes
import os
import re
import sys
import traceback
import types
import warnings
from http.client import responses
from http.cookies import Morsel
from urllib.parse import urlparse
from jinja2 import TemplateNotFound
from tornado import web, gen, escape, httputil
from tornado.log import app_log
import prometheus_client
from notebook._sysinfo import get_sys_info
from traitlets.config import Application
from ipython_genutils.path import filefind
from ipython_genutils.py3compat import string_types
import notebook
from notebook._tz import utcnow
from notebook.i18n import combine_translations
from notebook.utils import is_hidden, url_path_join, url_is_absolute, url_escape, urldecode_unix_socket_path
from notebook.services.security import csp_report_uri
#-----------------------------------------------------------------------------
# Top-level handlers
#-----------------------------------------------------------------------------
non_alphanum = re.compile(r'[^A-Za-z0-9]')
_sys_info_cache = None
def json_sys_info():
global _sys_info_cache
if _sys_info_cache is None:
_sys_info_cache = json.dumps(get_sys_info())
return _sys_info_cache
def log():
if Application.initialized():
return Application.instance().log
else:
return app_log
class AuthenticatedHandler(web.RequestHandler):
"""A RequestHandler with an authenticated user."""
@property
def content_security_policy(self):
"""The default Content-Security-Policy header
Can be overridden by defining Content-Security-Policy in settings['headers']
"""
if 'Content-Security-Policy' in self.settings.get('headers', {}):
# user-specified, don't override
return self.settings['headers']['Content-Security-Policy']
return '; '.join([
"frame-ancestors 'self'",
# Make sure the report-uri is relative to the base_url
"report-uri " + self.settings.get('csp_report_uri', url_path_join(self.base_url, csp_report_uri)),
])
def set_default_headers(self):
headers = {}
headers["X-Content-Type-Options"] = "nosniff"
headers.update(self.settings.get('headers', {}))
headers["Content-Security-Policy"] = self.content_security_policy
# Allow for overriding headers
for header_name, value in headers.items():
try:
self.set_header(header_name, value)
except Exception as e:
# tornado raise Exception (not a subclass)
# if method is unsupported (websocket and Access-Control-Allow-Origin
# for example, so just ignore)
self.log.debug(e)
def force_clear_cookie(self, name, path="/", domain=None):
"""Deletes the cookie with the given name.
Tornado's cookie handling currently (Jan 2018) stores cookies in a dict
keyed by name, so it can only modify one cookie with a given name per
response. The browser can store multiple cookies with the same name
but different domains and/or paths. This method lets us clear multiple
cookies with the same name.
Due to limitations of the cookie protocol, you must pass the same
path and domain to clear a cookie as were used when that cookie
was set (but there is no way to find out on the server side
which values were used for a given cookie).
"""
name = escape.native_str(name)
expires = datetime.datetime.utcnow() - datetime.timedelta(days=365)
morsel = Morsel()
morsel.set(name, '', '""')
morsel['expires'] = httputil.format_timestamp(expires)
morsel['path'] = path
if domain:
morsel['domain'] = domain
self.add_header("Set-Cookie", morsel.OutputString())
def clear_login_cookie(self):
cookie_options = self.settings.get('cookie_options', {})
path = cookie_options.setdefault('path', self.base_url)
self.clear_cookie(self.cookie_name, path=path)
if path and path != '/':
# also clear cookie on / to ensure old cookies are cleared
# after the change in path behavior (changed in notebook 5.2.2).
# N.B. This bypasses the normal cookie handling, which can't update
# two cookies with the same name. See the method above.
self.force_clear_cookie(self.cookie_name)
def get_current_user(self):
if self.login_handler is None:
return 'anonymous'
return self.login_handler.get_user(self)
def skip_check_origin(self):
"""Ask my login_handler if I should skip the origin_check
For example: in the default LoginHandler, if a request is token-authenticated,
origin checking should be skipped.
"""
if self.request.method == 'OPTIONS':
# no origin-check on options requests, which are used to check origins!
return True
if self.login_handler is None or not hasattr(self.login_handler, 'should_check_origin'):
return False
return not self.login_handler.should_check_origin(self)
@property
def token_authenticated(self):
"""Have I been authenticated with a token?"""
if self.login_handler is None or not hasattr(self.login_handler, 'is_token_authenticated'):
return False
return self.login_handler.is_token_authenticated(self)
@property
def cookie_name(self):
default_cookie_name = non_alphanum.sub('-', 'username-{}'.format(
self.request.host
))
return self.settings.get('cookie_name', default_cookie_name)
@property
def logged_in(self):
"""Is a user currently logged in?"""
user = self.get_current_user()
return (user and not user == 'anonymous')
@property
def login_handler(self):
"""Return the login handler for this application, if any."""
return self.settings.get('login_handler_class', None)
@property
def token(self):
"""Return the login token for this application, if any."""
return self.settings.get('token', None)
@property
def login_available(self):
"""May a user proceed to log in?
This returns True if login capability is available, irrespective of
whether the user is already logged in or not.
"""
if self.login_handler is None:
return False
return bool(self.login_handler.get_login_available(self.settings))
class IPythonHandler(AuthenticatedHandler):
"""IPython-specific extensions to authenticated handling
Mostly property shortcuts to IPython-specific settings.
"""
@property
def ignore_minified_js(self):
"""Wether to user bundle in template. (*.min files)
Mainly use for development and avoid file recompilation
"""
return self.settings.get('ignore_minified_js', False)
@property
def config(self):
return self.settings.get('config', None)
@property
def log(self):
"""use the IPython log by default, falling back on tornado's logger"""
return log()
@property
def jinja_template_vars(self):
"""User-supplied values to supply to jinja templates."""
return self.settings.get('jinja_template_vars', {})
#---------------------------------------------------------------
# URLs
#---------------------------------------------------------------
@property
def version_hash(self):
"""The version hash to use for cache hints for static files"""
return self.settings.get('version_hash', '')
@property
def mathjax_url(self):
url = self.settings.get('mathjax_url', '')
if not url or url_is_absolute(url):
return url
return url_path_join(self.base_url, url)
@property
def mathjax_config(self):
return self.settings.get('mathjax_config', 'TeX-AMS-MML_HTMLorMML-full,Safe')
@property
def base_url(self):
return self.settings.get('base_url', '/')
@property
def default_url(self):
return self.settings.get('default_url', '')
@property
def ws_url(self):
return self.settings.get('websocket_url', '')
@property
def contents_js_source(self):
self.log.debug("Using contents: %s", self.settings.get('contents_js_source',
'services/contents'))
return self.settings.get('contents_js_source', 'services/contents')
#---------------------------------------------------------------
# Manager objects
#---------------------------------------------------------------
@property
def kernel_manager(self):
return self.settings['kernel_manager']
@property
def contents_manager(self):
return self.settings['contents_manager']
@property
def session_manager(self):
return self.settings['session_manager']
@property
def terminal_manager(self):
return self.settings['terminal_manager']
@property
def kernel_spec_manager(self):
return self.settings['kernel_spec_manager']
@property
def config_manager(self):
return self.settings['config_manager']
#---------------------------------------------------------------
# CORS
#---------------------------------------------------------------
@property
def allow_origin(self):
"""Normal Access-Control-Allow-Origin"""
return self.settings.get('allow_origin', '')
@property
def allow_origin_pat(self):
"""Regular expression version of allow_origin"""
return self.settings.get('allow_origin_pat', None)
@property
def allow_credentials(self):
"""Whether to set Access-Control-Allow-Credentials"""
return self.settings.get('allow_credentials', False)
def set_default_headers(self):
"""Add CORS headers, if defined"""
super().set_default_headers()
if self.allow_origin:
self.set_header("Access-Control-Allow-Origin", self.allow_origin)
elif self.allow_origin_pat:
origin = self.get_origin()
if origin and self.allow_origin_pat.match(origin):
self.set_header("Access-Control-Allow-Origin", origin)
elif (
self.token_authenticated
and "Access-Control-Allow-Origin" not in
self.settings.get('headers', {})
):
# allow token-authenticated requests cross-origin by default.
# only apply this exception if allow-origin has not been specified.
self.set_header('Access-Control-Allow-Origin',
self.request.headers.get('Origin', ''))
if self.allow_credentials:
self.set_header("Access-Control-Allow-Credentials", 'true')
def set_attachment_header(self, filename):
"""Set Content-Disposition: attachment header
As a method to ensure handling of filename encoding
"""
escaped_filename = url_escape(filename)
self.set_header('Content-Disposition',
'attachment;'
" filename*=utf-8''{utf8}"
.format(
utf8=escaped_filename,
)
)
def get_origin(self):
# Handle WebSocket Origin naming convention differences
# The difference between version 8 and 13 is that in 8 the
# client sends a "Sec-Websocket-Origin" header and in 13 it's
# simply "Origin".
if "Origin" in self.request.headers:
origin = self.request.headers.get("Origin")
else:
origin = self.request.headers.get("Sec-Websocket-Origin", None)
return origin
# origin_to_satisfy_tornado is present because tornado requires
# check_origin to take an origin argument, but we don't use it
def check_origin(self, origin_to_satisfy_tornado=""):
"""Check Origin for cross-site API requests, including websockets
Copied from WebSocket with changes:
- allow unspecified host/origin (e.g. scripts)
- allow token-authenticated requests
"""
if self.allow_origin == '*' or self.skip_check_origin():
return True
host = self.request.headers.get("Host")
origin = self.request.headers.get("Origin")
# If no header is provided, let the request through.
# Origin can be None for:
# - same-origin (IE, Firefox)
# - Cross-site POST form (IE, Firefox)
# - Scripts
# The cross-site POST (XSRF) case is handled by tornado's xsrf_token
if origin is None or host is None:
return True
origin = origin.lower()
origin_host = urlparse(origin).netloc
# OK if origin matches host
if origin_host == host:
return True
# Check CORS headers
if self.allow_origin:
allow = self.allow_origin == origin
elif self.allow_origin_pat:
allow = bool(self.allow_origin_pat.match(origin))
else:
# No CORS headers deny the request
allow = False
if not allow:
self.log.warning("Blocking Cross Origin API request for %s. Origin: %s, Host: %s",
self.request.path, origin, host,
)
return allow
def check_referer(self):
"""Check Referer for cross-site requests.
Disables requests to certain endpoints with
external or missing Referer.
If set, allow_origin settings are applied to the Referer
to whitelist specific cross-origin sites.
Used on GET for api endpoints and /files/
to block cross-site inclusion (XSSI).
"""
if self.allow_origin == "*" or self.skip_check_origin():
return True
host = self.request.headers.get("Host")
referer = self.request.headers.get("Referer")
if not host:
self.log.warning("Blocking request with no host")
return False
if not referer:
self.log.warning("Blocking request with no referer")
return False
referer_url = urlparse(referer)
referer_host = referer_url.netloc
if referer_host == host:
return True
# apply cross-origin checks to Referer:
origin = "{}://{}".format(referer_url.scheme, referer_url.netloc)
if self.allow_origin:
allow = self.allow_origin == origin
elif self.allow_origin_pat:
allow = bool(self.allow_origin_pat.match(origin))
else:
# No CORS settings, deny the request
allow = False
if not allow:
self.log.warning("Blocking Cross Origin request for %s. Referer: %s, Host: %s",
self.request.path, origin, host,
)
return allow
def check_xsrf_cookie(self):
"""Bypass xsrf cookie checks when token-authenticated"""
if self.token_authenticated or self.settings.get('disable_check_xsrf', False):
# Token-authenticated requests do not need additional XSRF-check
# Servers without authentication are vulnerable to XSRF
return
try:
return super().check_xsrf_cookie()
except web.HTTPError as e:
if self.request.method in {'GET', 'HEAD'}:
# Consider Referer a sufficient cross-origin check for GET requests
if not self.check_referer():
referer = self.request.headers.get('Referer')
if referer:
msg = "Blocking Cross Origin request from {}.".format(referer)
else:
msg = "Blocking request from unknown origin"
raise web.HTTPError(403, msg) from e
else:
raise
def check_host(self):
"""Check the host header if remote access disallowed.
Returns True if the request should continue, False otherwise.
"""
if self.settings.get('allow_remote_access', False):
return True
# Remove port (e.g. ':8888') from host
host = re.match(r'^(.*?)(:\d+)?$', self.request.host).group(1)
# Browsers format IPv6 addresses like [::1]; we need to remove the []
if host.startswith('[') and host.endswith(']'):
host = host[1:-1]
# UNIX socket handling
check_host = urldecode_unix_socket_path(host)
if check_host.startswith('/') and os.path.exists(check_host):
allow = True
else:
try:
addr = ipaddress.ip_address(host)
except ValueError:
# Not an IP address: check against hostnames
allow = host in self.settings.get('local_hostnames', ['localhost'])
else:
allow = addr.is_loopback
if not allow:
self.log.warning(
("Blocking request with non-local 'Host' %s (%s). "
"If the notebook should be accessible at that name, "
"set NotebookApp.allow_remote_access to disable the check."),
host, self.request.host
)
return allow
def prepare(self):
if not self.check_host():
raise web.HTTPError(403)
return super().prepare()
#---------------------------------------------------------------
# template rendering
#---------------------------------------------------------------
def get_template(self, name):
"""Return the jinja template object for a given name"""
return self.settings['jinja2_env'].get_template(name)
def render_template(self, name, **ns):
ns.update(self.template_namespace)
template = self.get_template(name)
return template.render(**ns)
@property
def template_namespace(self):
return dict(
base_url=self.base_url,
default_url=self.default_url,
ws_url=self.ws_url,
logged_in=self.logged_in,
allow_password_change=self.settings.get('allow_password_change'),
login_available=self.login_available,
token_available=bool(self.token),
static_url=self.static_url,
sys_info=json_sys_info(),
contents_js_source=self.contents_js_source,
version_hash=self.version_hash,
ignore_minified_js=self.ignore_minified_js,
xsrf_form_html=self.xsrf_form_html,
token=self.token,
xsrf_token=self.xsrf_token.decode('utf8'),
nbjs_translations=json.dumps(combine_translations(
self.request.headers.get('Accept-Language', ''))),
**self.jinja_template_vars
)
def get_json_body(self):
"""Return the body of the request as JSON data."""
if not self.request.body:
return None
# Do we need to call body.decode('utf-8') here?
body = self.request.body.strip().decode(u'utf-8')
try:
model = json.loads(body)
except Exception as e:
self.log.debug("Bad JSON: %r", body)
self.log.error("Couldn't parse JSON", exc_info=True)
raise web.HTTPError(400, u'Invalid JSON in body of request') from e
return model
def write_error(self, status_code, **kwargs):
"""render custom error pages"""
exc_info = kwargs.get('exc_info')
message = ''
status_message = responses.get(status_code, 'Unknown HTTP Error')
exception = '(unknown)'
if exc_info:
exception = exc_info[1]
# get the custom message, if defined
try:
message = exception.log_message % exception.args
except Exception:
pass
# construct the custom reason, if defined
reason = getattr(exception, 'reason', '')
if reason:
status_message = reason
# build template namespace
ns = dict(
status_code=status_code,
status_message=status_message,
message=message,
exception=exception,
)
self.set_header('Content-Type', 'text/html')
# render the template
try:
html = self.render_template('%s.html' % status_code, **ns)
except TemplateNotFound:
html = self.render_template('error.html', **ns)
self.write(html)
class APIHandler(IPythonHandler):
"""Base class for API handlers"""
def prepare(self):
if not self.check_origin():
raise web.HTTPError(404)
return super().prepare()
def write_error(self, status_code, **kwargs):
"""APIHandler errors are JSON, not human pages"""
self.set_header('Content-Type', 'application/json')
message = responses.get(status_code, 'Unknown HTTP Error')
reply = {
'message': message,
}
exc_info = kwargs.get('exc_info')
if exc_info:
e = exc_info[1]
if isinstance(e, HTTPError):
reply['message'] = e.log_message or message
reply['reason'] = e.reason
else:
reply['message'] = 'Unhandled error'
reply['reason'] = None
reply['traceback'] = ''.join(traceback.format_exception(*exc_info))
self.log.warning(reply['message'])
self.finish(json.dumps(reply))
def get_current_user(self):
"""Raise 403 on API handlers instead of redirecting to human login page"""
# preserve _user_cache so we don't raise more than once
if hasattr(self, '_user_cache'):
return self._user_cache
self._user_cache = user = super().get_current_user()
return user
def get_login_url(self):
# if get_login_url is invoked in an API handler,
# that means @web.authenticated is trying to trigger a redirect.
# instead of redirecting, raise 403 instead.
if not self.current_user:
raise web.HTTPError(403)
return super().get_login_url()
@property
def content_security_policy(self):
csp = '; '.join([
super().content_security_policy,
"default-src 'none'",
])
return csp
# set _track_activity = False on API handlers that shouldn't track activity
_track_activity = True
def update_api_activity(self):
"""Update last_activity of API requests"""
# record activity of authenticated requests
if (
self._track_activity
and getattr(self, '_user_cache', None)
and self.get_argument('no_track_activity', None) is None
):
self.settings['api_last_activity'] = utcnow()
def finish(self, *args, **kwargs):
self.update_api_activity()
self.set_header('Content-Type', 'application/json')
return super().finish(*args, **kwargs)
def options(self, *args, **kwargs):
if 'Access-Control-Allow-Headers' in self.settings.get('headers', {}):
self.set_header('Access-Control-Allow-Headers', self.settings['headers']['Access-Control-Allow-Headers'])
else:
self.set_header('Access-Control-Allow-Headers',
'accept, content-type, authorization, x-xsrftoken')
self.set_header('Access-Control-Allow-Methods',
'GET, PUT, POST, PATCH, DELETE, OPTIONS')
# if authorization header is requested,
# that means the request is token-authenticated.
# avoid browser-side rejection of the preflight request.
# only allow this exception if allow_origin has not been specified
# and notebook authentication is enabled.
# If the token is not valid, the 'real' request will still be rejected.
requested_headers = self.request.headers.get('Access-Control-Request-Headers', '').split(',')
if requested_headers and any(
h.strip().lower() == 'authorization'
for h in requested_headers
) and (
# FIXME: it would be even better to check specifically for token-auth,
# but there is currently no API for this.
self.login_available
) and (
self.allow_origin
or self.allow_origin_pat
or 'Access-Control-Allow-Origin' in self.settings.get('headers', {})
):
self.set_header('Access-Control-Allow-Origin',
self.request.headers.get('Origin', ''))
class Template404(IPythonHandler):
"""Render our 404 template"""
def prepare(self):
raise web.HTTPError(404)
class AuthenticatedFileHandler(IPythonHandler, web.StaticFileHandler):
"""static files should only be accessible when logged in"""
@property
def content_security_policy(self):
# In case we're serving HTML/SVG, confine any Javascript to a unique
# origin so it can't interact with the notebook server.
return super().content_security_policy + "; sandbox allow-scripts"
@web.authenticated
def head(self, path):
self.check_xsrf_cookie()
return super().head(path)
@web.authenticated
def get(self, path):
self.check_xsrf_cookie()
if os.path.splitext(path)[1] == '.ipynb' or self.get_argument("download", False):
name = path.rsplit('/', 1)[-1]
self.set_attachment_header(name)
return web.StaticFileHandler.get(self, path)
def get_content_type(self):
path = self.absolute_path.strip('/')
if '/' in path:
_, name = path.rsplit('/', 1)
else:
name = path
if name.endswith('.ipynb'):
return 'application/x-ipynb+json'
else:
cur_mime = mimetypes.guess_type(name)[0]
if cur_mime == 'text/plain':
return 'text/plain; charset=UTF-8'
else:
return super().get_content_type()
def set_headers(self):
super().set_headers()
# disable browser caching, rely on 304 replies for savings
if "v" not in self.request.arguments:
self.add_header("Cache-Control", "no-cache")
def compute_etag(self):
return None
def validate_absolute_path(self, root, absolute_path):
"""Validate and return the absolute path.
Requires tornado 3.1
Adding to tornado's own handling, forbids the serving of hidden files.
"""
abs_path = super().validate_absolute_path(root, absolute_path)
abs_root = os.path.abspath(root)
if is_hidden(abs_path, abs_root) and not self.contents_manager.allow_hidden:
self.log.info("Refusing to serve hidden file, via 404 Error, use flag 'ContentsManager.allow_hidden' to enable")
raise web.HTTPError(404)
return abs_path
def json_errors(method):
"""Decorate methods with this to return GitHub style JSON errors.
This should be used on any JSON API on any handler method that can raise HTTPErrors.
This will grab the latest HTTPError exception using sys.exc_info
and then:
1. Set the HTTP status code based on the HTTPError
2. Create and return a JSON body with a message field describing
the error in a human readable form.
"""
warnings.warn('@json_errors is deprecated in notebook 5.2.0. Subclass APIHandler instead.',
DeprecationWarning,
stacklevel=2,
)
@functools.wraps(method)
def wrapper(self, *args, **kwargs):
self.write_error = types.MethodType(APIHandler.write_error, self)
return method(self, *args, **kwargs)
return wrapper
#-----------------------------------------------------------------------------
# File handler
#-----------------------------------------------------------------------------
# to minimize subclass changes:
HTTPError = web.HTTPError
class FileFindHandler(IPythonHandler, web.StaticFileHandler):
"""subclass of StaticFileHandler for serving files from a search path"""
# cache search results, don't search for files more than once
_static_paths = {}
def set_headers(self):
super().set_headers()
# disable browser caching, rely on 304 replies for savings
if "v" not in self.request.arguments or \
any(self.request.path.startswith(path) for path in self.no_cache_paths):
self.set_header("Cache-Control", "no-cache")
def initialize(self, path, default_filename=None, no_cache_paths=None):
self.no_cache_paths = no_cache_paths or []
if isinstance(path, string_types):
path = [path]
self.root = tuple(
os.path.abspath(os.path.expanduser(p)) + os.sep for p in path
)
self.default_filename = default_filename
def compute_etag(self):
return None
@classmethod
def get_absolute_path(cls, roots, path):
"""locate a file to serve on our static file search path"""
with cls._lock:
if path in cls._static_paths:
return cls._static_paths[path]
try:
abspath = os.path.abspath(filefind(path, roots))
except IOError:
# IOError means not found
return ''
cls._static_paths[path] = abspath
log().debug("Path %s served from %s"%(path, abspath))
return abspath
def validate_absolute_path(self, root, absolute_path):
"""check if the file should be served (raises 404, 403, etc.)"""
if absolute_path == '':
raise web.HTTPError(404)
for root in self.root:
if (absolute_path + os.sep).startswith(root):
break
return super().validate_absolute_path(root, absolute_path)
class APIVersionHandler(APIHandler):
def get(self):
# not authenticated, so give as few info as possible
self.finish(json.dumps({"version":notebook.__version__}))
class TrailingSlashHandler(web.RequestHandler):
"""Simple redirect handler that strips trailing slashes
This should be the first, highest priority handler.
"""
def get(self):
path, *rest = self.request.uri.partition("?")
# trim trailing *and* leading /
# to avoid misinterpreting repeated '//'
path = "/" + path.strip("/")
new_uri = "".join([path, *rest])
self.redirect(new_uri)
post = put = get
class FilesRedirectHandler(IPythonHandler):
"""Handler for redirecting relative URLs to the /files/ handler"""
@staticmethod
def redirect_to_files(self, path):
"""make redirect logic a reusable static method
so it can be called from other handlers.
"""
cm = self.contents_manager
if cm.dir_exists(path):
# it's a *directory*, redirect to /tree
url = url_path_join(self.base_url, 'tree', url_escape(path))
else:
orig_path = path
# otherwise, redirect to /files
parts = path.split('/')
if not cm.file_exists(path=path) and 'files' in parts:
# redirect without files/ iff it would 404
# this preserves pre-2.0-style 'files/' links
self.log.warning("Deprecated files/ URL: %s", orig_path)
parts.remove('files')
path = '/'.join(parts)
if not cm.file_exists(path=path):
raise web.HTTPError(404)
url = url_path_join(self.base_url, 'files', url_escape(path))
self.log.debug("Redirecting %s to %s", self.request.path, url)
self.redirect(url)
def get(self, path=''):
return self.redirect_to_files(self, path)
class RedirectWithParams(web.RequestHandler):
"""Sam as web.RedirectHandler, but preserves URL parameters"""
def initialize(self, url, permanent=True):
self._url = url
self._permanent = permanent
def get(self):
sep = '&' if '?' in self._url else '?'
url = sep.join([self._url, self.request.query])
self.redirect(url, permanent=self._permanent)
class PrometheusMetricsHandler(IPythonHandler):
"""
Return prometheus metrics for this notebook server
"""
def get(self):
if self.settings['authenticate_prometheus'] and not self.logged_in:
raise web.HTTPError(403)
self.set_header('Content-Type', prometheus_client.CONTENT_TYPE_LATEST)
self.write(prometheus_client.generate_latest(prometheus_client.REGISTRY))
#-----------------------------------------------------------------------------
# URL pattern fragments for re-use
#-----------------------------------------------------------------------------
# path matches any number of `/foo[/bar...]` or just `/` or ''
path_regex = r"(?P<path>(?:(?:/[^/]+)+|/?))"
#-----------------------------------------------------------------------------
# URL to handler mappings
#-----------------------------------------------------------------------------
default_handlers = [
(r".*/", TrailingSlashHandler),
(r"api", APIVersionHandler),
(r'/(robots\.txt|favicon\.ico)', web.StaticFileHandler),
(r'/metrics', PrometheusMetricsHandler)
]

@ -1,302 +0,0 @@
"""Tornado handlers for WebSocket <-> ZMQ sockets."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import json
import struct
import sys
from urllib.parse import urlparse
import tornado
from tornado import gen, ioloop, web
from tornado.iostream import StreamClosedError
from tornado.websocket import WebSocketHandler, WebSocketClosedError
from jupyter_client.session import Session
try:
from jupyter_client.jsonutil import json_default, extract_dates
except ImportError:
from jupyter_client.jsonutil import (
date_default as json_default, extract_dates
)
from ipython_genutils.py3compat import cast_unicode
from notebook.utils import maybe_future
from .handlers import IPythonHandler
def serialize_binary_message(msg):
"""serialize a message as a binary blob
Header:
4 bytes: number of msg parts (nbufs) as 32b int
4 * nbufs bytes: offset for each buffer as integer as 32b int
Offsets are from the start of the buffer, including the header.
Returns
-------
The message serialized to bytes.
"""
# don't modify msg or buffer list in-place
msg = msg.copy()
buffers = list(msg.pop('buffers'))
bmsg = json.dumps(msg, default=json_default).encode('utf8')
buffers.insert(0, bmsg)
nbufs = len(buffers)
offsets = [4 * (nbufs + 1)]
for buf in buffers[:-1]:
offsets.append(offsets[-1] + len(buf))
offsets_buf = struct.pack('!' + 'I' * (nbufs + 1), nbufs, *offsets)
buffers.insert(0, offsets_buf)
return b''.join(buffers)
def deserialize_binary_message(bmsg):
"""deserialize a message from a binary blog
Header:
4 bytes: number of msg parts (nbufs) as 32b int
4 * nbufs bytes: offset for each buffer as integer as 32b int
Offsets are from the start of the buffer, including the header.
Returns
-------
message dictionary
"""
nbufs = struct.unpack('!i', bmsg[:4])[0]
offsets = list(struct.unpack('!' + 'I' * nbufs, bmsg[4:4*(nbufs+1)]))
offsets.append(None)
bufs = []
for start, stop in zip(offsets[:-1], offsets[1:]):
bufs.append(bmsg[start:stop])
msg = json.loads(bufs[0].decode('utf8'))
msg['header'] = extract_dates(msg['header'])
msg['parent_header'] = extract_dates(msg['parent_header'])
msg['buffers'] = bufs[1:]
return msg
# ping interval for keeping websockets alive (30 seconds)
WS_PING_INTERVAL = 30000
class WebSocketMixin(object):
"""Mixin for common websocket options"""
ping_callback = None
last_ping = 0
last_pong = 0
stream = None
@property
def ping_interval(self):
"""The interval for websocket keep-alive pings.
Set ws_ping_interval = 0 to disable pings.
"""
return self.settings.get('ws_ping_interval', WS_PING_INTERVAL)
@property
def ping_timeout(self):
"""If no ping is received in this many milliseconds,
close the websocket connection (VPNs, etc. can fail to cleanly close ws connections).
Default is max of 3 pings or 30 seconds.
"""
return self.settings.get('ws_ping_timeout',
max(3 * self.ping_interval, WS_PING_INTERVAL)
)
def check_origin(self, origin=None):
"""Check Origin == Host or Access-Control-Allow-Origin.
Tornado >= 4 calls this method automatically, raising 403 if it returns False.
"""
if self.allow_origin == '*' or (
hasattr(self, 'skip_check_origin') and self.skip_check_origin()):
return True
host = self.request.headers.get("Host")
if origin is None:
origin = self.get_origin()
# If no origin or host header is provided, assume from script
if origin is None or host is None:
return True
origin = origin.lower()
origin_host = urlparse(origin).netloc
# OK if origin matches host
if origin_host == host:
return True
# Check CORS headers
if self.allow_origin:
allow = self.allow_origin == origin
elif self.allow_origin_pat:
allow = bool(self.allow_origin_pat.match(origin))
else:
# No CORS headers deny the request
allow = False
if not allow:
self.log.warning("Blocking Cross Origin WebSocket Attempt. Origin: %s, Host: %s",
origin, host,
)
return allow
def clear_cookie(self, *args, **kwargs):
"""meaningless for websockets"""
pass
def open(self, *args, **kwargs):
self.log.debug("Opening websocket %s", self.request.path)
# start the pinging
if self.ping_interval > 0:
loop = ioloop.IOLoop.current()
self.last_ping = loop.time() # Remember time of last ping
self.last_pong = self.last_ping
self.ping_callback = ioloop.PeriodicCallback(
self.send_ping, self.ping_interval,
)
self.ping_callback.start()
return super(WebSocketMixin, self).open(*args, **kwargs)
def send_ping(self):
"""send a ping to keep the websocket alive"""
if self.ws_connection is None and self.ping_callback is not None:
self.ping_callback.stop()
return
# check for timeout on pong. Make sure that we really have sent a recent ping in
# case the machine with both server and client has been suspended since the last ping.
now = ioloop.IOLoop.current().time()
since_last_pong = 1e3 * (now - self.last_pong)
since_last_ping = 1e3 * (now - self.last_ping)
if since_last_ping < 2*self.ping_interval and since_last_pong > self.ping_timeout:
self.log.warning("WebSocket ping timeout after %i ms.", since_last_pong)
self.close()
return
try:
self.ping(b'')
except (StreamClosedError, WebSocketClosedError):
# websocket has been closed, stop pinging
self.ping_callback.stop()
return
self.last_ping = now
def on_pong(self, data):
self.last_pong = ioloop.IOLoop.current().time()
class ZMQStreamHandler(WebSocketMixin, WebSocketHandler):
if tornado.version_info < (4,1):
"""Backport send_error from tornado 4.1 to 4.0"""
def send_error(self, *args, **kwargs):
if self.stream is None:
super(WebSocketHandler, self).send_error(*args, **kwargs)
else:
# If we get an uncaught exception during the handshake,
# we have no choice but to abruptly close the connection.
# TODO: for uncaught exceptions after the handshake,
# we can close the connection more gracefully.
self.stream.close()
def _reserialize_reply(self, msg_or_list, channel=None):
"""Reserialize a reply message using JSON.
msg_or_list can be an already-deserialized msg dict or the zmq buffer list.
If it is the zmq list, it will be deserialized with self.session.
This takes the msg list from the ZMQ socket and serializes the result for the websocket.
This method should be used by self._on_zmq_reply to build messages that can
be sent back to the browser.
"""
if isinstance(msg_or_list, dict):
# already unpacked
msg = msg_or_list
else:
idents, msg_list = self.session.feed_identities(msg_or_list)
msg = self.session.deserialize(msg_list)
if channel:
msg['channel'] = channel
if msg['buffers']:
buf = serialize_binary_message(msg)
return buf
else:
smsg = json.dumps(msg, default=json_default)
return cast_unicode(smsg)
def _on_zmq_reply(self, stream, msg_list):
# Sometimes this gets triggered when the on_close method is scheduled in the
# eventloop but hasn't been called.
if self.ws_connection is None or stream.closed():
self.log.warning("zmq message arrived on closed channel")
self.close()
return
channel = getattr(stream, 'channel', None)
try:
msg = self._reserialize_reply(msg_list, channel=channel)
except Exception:
self.log.critical("Malformed message: %r" % msg_list, exc_info=True)
return
try:
self.write_message(msg, binary=isinstance(msg, bytes))
except (StreamClosedError, WebSocketClosedError):
self.log.warning("zmq message arrived on closed channel")
self.close()
return
class AuthenticatedZMQStreamHandler(ZMQStreamHandler, IPythonHandler):
def set_default_headers(self):
"""Undo the set_default_headers in IPythonHandler
which doesn't make sense for websockets
"""
pass
def pre_get(self):
"""Run before finishing the GET request
Extend this method to add logic that should fire before
the websocket finishes completing.
"""
# authenticate the request before opening the websocket
if self.get_current_user() is None:
self.log.warning("Couldn't authenticate WebSocket connection")
raise web.HTTPError(403)
if self.get_argument('session_id', False):
self.session.session = cast_unicode(self.get_argument('session_id'))
else:
self.log.warning("No session ID specified")
@gen.coroutine
def get(self, *args, **kwargs):
# pre_get can be a coroutine in subclasses
# assign and yield in two step to avoid tornado 3 issues
res = self.pre_get()
yield maybe_future(res)
res = super().get(*args, **kwargs)
yield maybe_future(res)
def initialize(self):
self.log.debug("Initializing websocket connection %s", self.request.path)
self.session = Session(config=self.config)
def get_compression_options(self):
return self.settings.get('websocket_compression_options', None)

@ -1,7 +0,0 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from .bundlerextensions import main
if __name__ == '__main__':
main()

@ -1,307 +0,0 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import sys
import os
from ..extensions import BaseExtensionApp, _get_config_dir, GREEN_ENABLED, RED_DISABLED
from .._version import __version__
from notebook.config_manager import BaseJSONConfigManager
from jupyter_core.paths import jupyter_config_path
from traitlets.utils.importstring import import_item
from traitlets import Bool
BUNDLER_SECTION = "notebook"
BUNDLER_SUBSECTION = "bundlerextensions"
def _get_bundler_metadata(module):
"""Gets the list of bundlers associated with a Python package.
Returns a tuple of (the module, [{
'name': 'unique name of the bundler',
'label': 'file menu item label for the bundler',
'module_name': 'dotted package/module name containing the bundler',
'group': 'download or deploy parent menu item'
}])
Parameters
----------
module : str
Importable Python module exposing the
magic-named `_jupyter_bundlerextension_paths` function
"""
m = import_item(module)
if not hasattr(m, '_jupyter_bundlerextension_paths'):
raise KeyError('The Python module {} does not contain a valid bundlerextension'.format(module))
bundlers = m._jupyter_bundlerextension_paths()
return m, bundlers
def _set_bundler_state(name, label, module_name, group, state,
user=True, sys_prefix=False, logger=None):
"""Set whether a bundler is enabled or disabled.
Returns True if the final state is the one requested.
Parameters
----------
name : string
Unique name of the bundler
label : string
Human-readable label for the bundler menu item in the notebook UI
module_name : string
Dotted module/package name containing the bundler
group : string
'download' or 'deploy' indicating the parent menu containing the label
state : bool
The state in which to leave the extension
user : bool [default: True]
Whether to update the user's .jupyter/nbconfig directory
sys_prefix : bool [default: False]
Whether to update the sys.prefix, i.e. environment. Will override
`user`.
logger : Jupyter logger [optional]
Logger instance to use
"""
user = False if sys_prefix else user
config_dir = os.path.join(
_get_config_dir(user=user, sys_prefix=sys_prefix), 'nbconfig')
cm = BaseJSONConfigManager(config_dir=config_dir)
if logger:
logger.info("{} {} bundler {}...".format(
"Enabling" if state else "Disabling",
name,
module_name
))
if state:
cm.update(BUNDLER_SECTION, {
BUNDLER_SUBSECTION: {
name: {
"label": label,
"module_name": module_name,
"group" : group
}
}
})
else:
cm.update(BUNDLER_SECTION, {
BUNDLER_SUBSECTION: {
name: None
}
})
return (cm.get(BUNDLER_SECTION)
.get(BUNDLER_SUBSECTION, {})
.get(name) is not None) == state
def _set_bundler_state_python(state, module, user, sys_prefix, logger=None):
"""Enables or disables bundlers defined in a Python package.
Returns a list of whether the state was achieved for each bundler.
Parameters
----------
state : Bool
Whether the extensions should be enabled
module : str
Importable Python module exposing the
magic-named `_jupyter_bundlerextension_paths` function
user : bool
Whether to enable in the user's nbconfig directory.
sys_prefix : bool
Enable/disable in the sys.prefix, i.e. environment
logger : Jupyter logger [optional]
Logger instance to use
"""
m, bundlers = _get_bundler_metadata(module)
return [_set_bundler_state(name=bundler["name"],
label=bundler["label"],
module_name=bundler["module_name"],
group=bundler["group"],
state=state,
user=user, sys_prefix=sys_prefix,
logger=logger)
for bundler in bundlers]
def enable_bundler_python(module, user=True, sys_prefix=False, logger=None):
"""Enables bundlers defined in a Python package.
Returns whether each bundle defined in the packaged was enabled or not.
Parameters
----------
module : str
Importable Python module exposing the
magic-named `_jupyter_bundlerextension_paths` function
user : bool [default: True]
Whether to enable in the user's nbconfig directory.
sys_prefix : bool [default: False]
Whether to enable in the sys.prefix, i.e. environment. Will override
`user`
logger : Jupyter logger [optional]
Logger instance to use
"""
return _set_bundler_state_python(True, module, user, sys_prefix,
logger=logger)
def disable_bundler_python(module, user=True, sys_prefix=False, logger=None):
"""Disables bundlers defined in a Python package.
Returns whether each bundle defined in the packaged was enabled or not.
Parameters
----------
module : str
Importable Python module exposing the
magic-named `_jupyter_bundlerextension_paths` function
user : bool [default: True]
Whether to enable in the user's nbconfig directory.
sys_prefix : bool [default: False]
Whether to enable in the sys.prefix, i.e. environment. Will override
`user`
logger : Jupyter logger [optional]
Logger instance to use
"""
return _set_bundler_state_python(False, module, user, sys_prefix,
logger=logger)
class ToggleBundlerExtensionApp(BaseExtensionApp):
"""A base class for apps that enable/disable bundlerextensions"""
name = "jupyter bundlerextension enable/disable"
version = __version__
description = "Enable/disable a bundlerextension in configuration."
user = Bool(True, config=True, help="Apply the configuration only for the current user (default)")
_toggle_value = None
def _config_file_name_default(self):
"""The default config file name."""
return 'jupyter_notebook_config'
def toggle_bundler_python(self, module):
"""Toggle some extensions in an importable Python module.
Returns a list of booleans indicating whether the state was changed as
requested.
Parameters
----------
module : str
Importable Python module exposing the
magic-named `_jupyter_bundlerextension_paths` function
"""
toggle = (enable_bundler_python if self._toggle_value
else disable_bundler_python)
return toggle(module,
user=self.user,
sys_prefix=self.sys_prefix,
logger=self.log)
def start(self):
if not self.extra_args:
sys.exit('Please specify an bundlerextension/package to enable or disable')
elif len(self.extra_args) > 1:
sys.exit('Please specify one bundlerextension/package at a time')
if self.python:
self.toggle_bundler_python(self.extra_args[0])
else:
raise NotImplementedError('Cannot install bundlers from non-Python packages')
class EnableBundlerExtensionApp(ToggleBundlerExtensionApp):
"""An App that enables bundlerextensions"""
name = "jupyter bundlerextension enable"
description = """
Enable a bundlerextension in frontend configuration.
Usage
jupyter bundlerextension enable [--system|--sys-prefix]
"""
_toggle_value = True
class DisableBundlerExtensionApp(ToggleBundlerExtensionApp):
"""An App that disables bundlerextensions"""
name = "jupyter bundlerextension disable"
description = """
Disable a bundlerextension in frontend configuration.
Usage
jupyter bundlerextension disable [--system|--sys-prefix]
"""
_toggle_value = None
class ListBundlerExtensionApp(BaseExtensionApp):
"""An App that lists and validates nbextensions"""
name = "jupyter nbextension list"
version = __version__
description = "List all nbextensions known by the configuration system"
def list_nbextensions(self):
"""List all the nbextensions"""
config_dirs = [os.path.join(p, 'nbconfig') for p in jupyter_config_path()]
print("Known bundlerextensions:")
for config_dir in config_dirs:
head = u' config dir: {}'.format(config_dir)
head_shown = False
cm = BaseJSONConfigManager(parent=self, config_dir=config_dir)
data = cm.get('notebook')
if 'bundlerextensions' in data:
if not head_shown:
# only show heading if there is an nbextension here
print(head)
head_shown = True
for bundler_id, info in data['bundlerextensions'].items():
label = info.get('label')
module = info.get('module_name')
if label is None or module is None:
msg = u' {} {}'.format(bundler_id, RED_DISABLED)
else:
msg = u' "{}" from {} {}'.format(
label, module, GREEN_ENABLED
)
print(msg)
def start(self):
"""Perform the App's functions as configured"""
self.list_nbextensions()
class BundlerExtensionApp(BaseExtensionApp):
"""Base jupyter bundlerextension command entry point"""
name = "jupyter bundlerextension"
version = __version__
description = "Work with Jupyter bundler extensions"
examples = """
jupyter bundlerextension list # list all configured bundlers
jupyter bundlerextension enable --py <packagename> # enable all bundlers in a Python package
jupyter bundlerextension disable --py <packagename> # disable all bundlers in a Python package
"""
subcommands = dict(
enable=(EnableBundlerExtensionApp, "Enable a bundler extension"),
disable=(DisableBundlerExtensionApp, "Disable a bundler extension"),
list=(ListBundlerExtensionApp, "List bundler extensions")
)
def start(self):
"""Perform the App's functions as configured"""
super().start()
# The above should have called a subcommand and raised NoStart; if we
# get here, it didn't, so we should self.log.info a message.
subcmds = ", ".join(sorted(self.subcommands))
sys.exit("Please supply at least one subcommand: %s" % subcmds)
main = BundlerExtensionApp.launch_instance
if __name__ == '__main__':
main()

@ -1,88 +0,0 @@
"""Tornado handler for bundling notebooks."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from ipython_genutils.importstring import import_item
from tornado import web, gen
from notebook.utils import maybe_future, url2path
from notebook.base.handlers import IPythonHandler
from notebook.services.config import ConfigManager
from . import tools
class BundlerHandler(IPythonHandler):
def initialize(self):
"""Make tools module available on the handler instance for compatibility
with existing bundler API and ease of reference."""
self.tools = tools
def get_bundler(self, bundler_id):
"""
Get bundler metadata from config given a bundler ID.
Parameters
----------
bundler_id: str
Unique bundler ID within the notebook/bundlerextensions config section
Returns
-------
dict
Bundler metadata with label, group, and module_name attributes
Raises
------
KeyError
If the bundler ID is unknown
"""
cm = ConfigManager()
return cm.get('notebook').get('bundlerextensions', {})[bundler_id]
@web.authenticated
@gen.coroutine
def get(self, path):
"""Bundle the given notebook.
Parameters
----------
path: str
Path to the notebook (path parameter)
bundler: str
Bundler ID to use (query parameter)
"""
bundler_id = self.get_query_argument('bundler')
model = self.contents_manager.get(path=url2path(path))
try:
bundler = self.get_bundler(bundler_id)
except KeyError as e:
raise web.HTTPError(400, 'Bundler %s not enabled' %
bundler_id) from e
module_name = bundler['module_name']
try:
# no-op in python3, decode error in python2
module_name = str(module_name)
except UnicodeEncodeError:
# Encode unicode as utf-8 in python2 else import_item fails
module_name = module_name.encode('utf-8')
try:
bundler_mod = import_item(module_name)
except ImportError as e:
raise web.HTTPError(500, 'Could not import bundler %s ' %
bundler_id) from e
# Let the bundler respond in any way it sees fit and assume it will
# finish the request
yield maybe_future(bundler_mod.bundle(self, model))
_bundler_id_regex = r'(?P<bundler_id>[A-Za-z0-9_]+)'
default_handlers = [
(r"/bundle/(.*)", BundlerHandler)
]

@ -1,47 +0,0 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import os
import io
import tarfile
import nbformat
def _jupyter_bundlerextension_paths():
"""Metadata for notebook bundlerextension"""
return [{
# unique bundler name
"name": "tarball_bundler",
# module containing bundle function
"module_name": "notebook.bundler.tarball_bundler",
# human-readable menu item label
"label" : "Notebook Tarball (tar.gz)",
# group under 'deploy' or 'download' menu
"group" : "download",
}]
def bundle(handler, model):
"""Create a compressed tarball containing the notebook document.
Parameters
----------
handler : tornado.web.RequestHandler
Handler that serviced the bundle request
model : dict
Notebook model from the configured ContentManager
"""
notebook_filename = model['name']
notebook_content = nbformat.writes(model['content']).encode('utf-8')
notebook_name = os.path.splitext(notebook_filename)[0]
tar_filename = '{}.tar.gz'.format(notebook_name)
info = tarfile.TarInfo(notebook_filename)
info.size = len(notebook_content)
with io.BytesIO() as tar_buffer:
with tarfile.open(tar_filename, "w:gz", fileobj=tar_buffer) as tar:
tar.addfile(info, io.BytesIO(notebook_content))
handler.set_attachment_header(tar_filename)
handler.set_header('Content-Type', 'application/gzip')
# Return the buffer value as the response
handler.finish(tar_buffer.getvalue())

@ -1,6 +0,0 @@
{
"nbformat_minor": 0,
"cells": [],
"nbformat": 4,
"metadata": {}
}

@ -1,80 +0,0 @@
"""Test the bundlers API."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import io
from os.path import join as pjoin
from notebook.tests.launchnotebook import NotebookTestBase
from nbformat import write
from nbformat.v4 import (
new_notebook, new_markdown_cell, new_code_cell, new_output,
)
from unittest.mock import patch
def bundle(handler, model):
"""Bundler test stub. Echo the notebook path."""
handler.finish(model['path'])
class BundleAPITest(NotebookTestBase):
"""Test the bundlers web service API"""
@classmethod
def setup_class(cls):
"""Make a test notebook. Borrowed from nbconvert test. Assumes the class
teardown will clean it up in the end."""
super(BundleAPITest, cls).setup_class()
nbdir = cls.notebook_dir
nb = new_notebook()
nb.cells.append(new_markdown_cell(u'Created by test'))
cc1 = new_code_cell(source=u'print(2*6)')
cc1.outputs.append(new_output(output_type="stream", text=u'12'))
nb.cells.append(cc1)
with io.open(pjoin(nbdir, 'testnb.ipynb'), 'w',
encoding='utf-8') as f:
write(nb, f, version=4)
def test_missing_bundler_arg(self):
"""Should respond with 400 error about missing bundler arg"""
resp = self.request('GET', 'bundle/fake.ipynb')
self.assertEqual(resp.status_code, 400)
self.assertIn('Missing argument bundler', resp.text)
def test_notebook_not_found(self):
"""Should respond with 404 error about missing notebook"""
resp = self.request('GET', 'bundle/fake.ipynb',
params={'bundler': 'fake_bundler'})
self.assertEqual(resp.status_code, 404)
self.assertIn('Not Found', resp.text)
def test_bundler_not_enabled(self):
"""Should respond with 400 error about disabled bundler"""
resp = self.request('GET', 'bundle/testnb.ipynb',
params={'bundler': 'fake_bundler'})
self.assertEqual(resp.status_code, 400)
self.assertIn('Bundler fake_bundler not enabled', resp.text)
def test_bundler_import_error(self):
"""Should respond with 500 error about failure to load bundler module"""
with patch('notebook.bundler.handlers.BundlerHandler.get_bundler') as mock:
mock.return_value = {'module_name': 'fake_module'}
resp = self.request('GET', 'bundle/testnb.ipynb',
params={'bundler': 'fake_bundler'})
mock.assert_called_with('fake_bundler')
self.assertEqual(resp.status_code, 500)
self.assertIn('Could not import bundler fake_bundler', resp.text)
def test_bundler_invoke(self):
"""Should respond with 200 and output from test bundler stub"""
with patch('notebook.bundler.handlers.BundlerHandler.get_bundler') as mock:
mock.return_value = {'module_name': 'notebook.bundler.tests.test_bundler_api'}
resp = self.request('GET', 'bundle/testnb.ipynb',
params={'bundler': 'stub_bundler'})
mock.assert_called_with('stub_bundler')
self.assertEqual(resp.status_code, 200)
self.assertIn('testnb.ipynb', resp.text)

@ -1,124 +0,0 @@
"""Test the bundler tools."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import unittest
import os
import shutil
import tempfile
import notebook.bundler.tools as tools
HERE = os.path.abspath(os.path.dirname(__file__))
class TestBundlerTools(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.mkdtemp()
def tearDown(self):
shutil.rmtree(self.tmp, ignore_errors=True)
def test_get_no_cell_references(self):
'''Should find no references in a regular HTML comment.'''
no_references = tools.get_cell_reference_patterns({'source':'''!<--
a
b
c
-->''', 'cell_type':'markdown'})
self.assertEqual(len(no_references), 0)
def test_get_cell_reference_patterns_comment_multiline(self):
'''Should find two references and ignore a comment within an HTML comment.'''
cell = {'cell_type':'markdown', 'source':'''<!--associate:
a
b/
#comment
-->'''}
references = tools.get_cell_reference_patterns(cell)
self.assertTrue('a' in references and 'b/' in references, str(references))
self.assertEqual(len(references), 2, str(references))
def test_get_cell_reference_patterns_comment_trailing_filename(self):
'''Should find three references within an HTML comment.'''
cell = {'cell_type':'markdown', 'source':'''<!--associate:c
a
b/
#comment
-->'''}
references = tools.get_cell_reference_patterns(cell)
self.assertTrue('a' in references and 'b/' in references and 'c' in references, str(references))
self.assertEqual(len(references), 3, str(references))
def test_get_cell_reference_patterns_precode(self):
'''Should find no references in a fenced code block in a *code* cell.'''
self.assertTrue(tools.get_cell_reference_patterns)
no_references = tools.get_cell_reference_patterns({'source':'''```
foo
bar
baz
```
''', 'cell_type':'code'})
self.assertEqual(len(no_references), 0)
def test_get_cell_reference_patterns_precode_mdcomment(self):
'''Should find two references and ignore a comment in a fenced code block.'''
cell = {'cell_type':'markdown', 'source':'''```
a
b/
#comment
```'''}
references = tools.get_cell_reference_patterns(cell)
self.assertTrue('a' in references and 'b/' in references, str(references))
self.assertEqual(len(references), 2, str(references))
def test_get_cell_reference_patterns_precode_backticks(self):
'''Should find three references in a fenced code block.'''
cell = {'cell_type':'markdown', 'source':'''```c
a
b/
#comment
```'''}
references = tools.get_cell_reference_patterns(cell)
self.assertTrue('a' in references and 'b/' in references and 'c' in references, str(references))
self.assertEqual(len(references), 3, str(references))
def test_glob_dir(self):
'''Should expand to single file in the resources/ subfolder.'''
self.assertIn(os.path.join('resources', 'empty.ipynb'),
tools.expand_references(HERE, ['resources/empty.ipynb']))
def test_glob_subdir(self):
'''Should expand to all files in the resources/ subfolder.'''
self.assertIn(os.path.join('resources', 'empty.ipynb'),
tools.expand_references(HERE, ['resources/']))
def test_glob_splat(self):
'''Should expand to all contents under this test/ directory.'''
globs = tools.expand_references(HERE, ['*'])
self.assertIn('test_bundler_tools.py', globs, globs)
self.assertIn('resources', globs, globs)
def test_glob_splatsplat_in_middle(self):
'''Should expand to test_file.txt deep under this test/ directory.'''
globs = tools.expand_references(HERE, ['resources/**/test_file.txt'])
self.assertIn(os.path.join('resources', 'subdir', 'test_file.txt'), globs, globs)
def test_glob_splatsplat_trailing(self):
'''Should expand to all descendants of this test/ directory.'''
globs = tools.expand_references(HERE, ['resources/**'])
self.assertIn(os.path.join('resources', 'empty.ipynb'), globs, globs)
self.assertIn(os.path.join('resources', 'subdir', 'test_file.txt'), globs, globs)
def test_glob_splatsplat_leading(self):
'''Should expand to test_file.txt under any path.'''
globs = tools.expand_references(HERE, ['**/test_file.txt'])
self.assertIn(os.path.join('resources', 'subdir', 'test_file.txt'), globs, globs)
self.assertIn(os.path.join('resources', 'another_subdir', 'test_file.txt'), globs, globs)
def test_copy_filelist(self):
'''Should copy select files from source to destination'''
globs = tools.expand_references(HERE, ['**/test_file.txt'])
tools.copy_filelist(HERE, self.tmp, globs)
self.assertTrue(os.path.isfile(os.path.join(self.tmp, 'resources', 'subdir', 'test_file.txt')))
self.assertTrue(os.path.isfile(os.path.join(self.tmp, 'resources', 'another_subdir', 'test_file.txt')))
self.assertFalse(os.path.isfile(os.path.join(self.tmp, 'resources', 'empty.ipynb')))

@ -1,72 +0,0 @@
"""Test the bundlerextension CLI."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import os
import shutil
import unittest
from unittest.mock import patch
from ipython_genutils.tempdir import TemporaryDirectory
from ipython_genutils import py3compat
from traitlets.tests.utils import check_help_all_output
import notebook.nbextensions as nbextensions
from notebook.config_manager import BaseJSONConfigManager
from ..bundlerextensions import (_get_config_dir, enable_bundler_python,
disable_bundler_python)
def test_help_output():
check_help_all_output('notebook.bundler.bundlerextensions')
check_help_all_output('notebook.bundler.bundlerextensions', ['enable'])
check_help_all_output('notebook.bundler.bundlerextensions', ['disable'])
class TestBundlerExtensionCLI(unittest.TestCase):
"""Tests the bundlerextension CLI against the example zip_bundler."""
def setUp(self):
"""Build an isolated config environment."""
td = TemporaryDirectory()
self.test_dir = py3compat.cast_unicode(td.name)
self.data_dir = os.path.join(self.test_dir, 'data')
self.config_dir = os.path.join(self.test_dir, 'config')
self.system_data_dir = os.path.join(self.test_dir, 'system_data')
self.system_path = [self.system_data_dir]
# Use temp directory, not real user or system config paths
self.patch_env = patch.dict('os.environ', {
'JUPYTER_CONFIG_DIR': self.config_dir,
'JUPYTER_DATA_DIR': self.data_dir,
})
self.patch_env.start()
self.patch_system_path = patch.object(nbextensions,
'SYSTEM_JUPYTER_PATH', self.system_path)
self.patch_system_path.start()
def tearDown(self):
"""Remove the test config environment."""
shutil.rmtree(self.test_dir, ignore_errors=True)
self.patch_env.stop()
self.patch_system_path.stop()
def test_enable(self):
"""Should add the bundler to the notebook configuration."""
enable_bundler_python('notebook.bundler.zip_bundler')
config_dir = os.path.join(_get_config_dir(user=True), 'nbconfig')
cm = BaseJSONConfigManager(config_dir=config_dir)
bundlers = cm.get('notebook').get('bundlerextensions', {})
self.assertEqual(len(bundlers), 1)
self.assertIn('notebook_zip_download', bundlers)
def test_disable(self):
"""Should remove the bundler from the notebook configuration."""
self.test_enable()
disable_bundler_python('notebook.bundler.zip_bundler')
config_dir = os.path.join(_get_config_dir(user=True), 'nbconfig')
cm = BaseJSONConfigManager(config_dir=config_dir)
bundlers = cm.get('notebook').get('bundlerextensions', {})
self.assertEqual(len(bundlers), 0)

@ -1,230 +0,0 @@
"""Set of common tools to aid bundler implementations."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import os
import shutil
import errno
import nbformat
import fnmatch
import glob
def get_file_references(abs_nb_path, version):
"""Gets a list of files referenced either in Markdown fenced code blocks
or in HTML comments from the notebook. Expands patterns expressed in
gitignore syntax (https://git-scm.com/docs/gitignore). Returns the
fully expanded list of filenames relative to the notebook dirname.
Parameters
----------
abs_nb_path: str
Absolute path of the notebook on disk
version: int
Version of the notebook document format to use
Returns
-------
list
Filename strings relative to the notebook path
"""
ref_patterns = get_reference_patterns(abs_nb_path, version)
expanded = expand_references(os.path.dirname(abs_nb_path), ref_patterns)
return expanded
def get_reference_patterns(abs_nb_path, version):
"""Gets a list of reference patterns either in Markdown fenced code blocks
or in HTML comments from the notebook.
Parameters
----------
abs_nb_path: str
Absolute path of the notebook on disk
version: int
Version of the notebook document format to use
Returns
-------
list
Pattern strings from the notebook
"""
notebook = nbformat.read(abs_nb_path, version)
referenced_list = []
for cell in notebook.cells:
references = get_cell_reference_patterns(cell)
if references:
referenced_list = referenced_list + references
return referenced_list
def get_cell_reference_patterns(cell):
'''
Retrieves the list of references from a single notebook cell. Looks for
fenced code blocks or HTML comments in Markdown cells, e.g.,
```
some.csv
foo/
!foo/bar
```
or
<!--associate:
some.csv
foo/
!foo/bar
-->
Parameters
----------
cell: dict
Notebook cell object
Returns
-------
list
Reference patterns found in the cell
'''
referenced = []
# invisible after execution: unrendered HTML comment
if cell.get('cell_type').startswith('markdown') and cell.get('source').startswith('<!--associate:'):
lines = cell.get('source')[len('<!--associate:'):].splitlines()
for line in lines:
if line.startswith('-->'):
break
# Trying to go out of the current directory leads to
# trouble when deploying
if line.find('../') < 0 and not line.startswith('#'):
referenced.append(line)
# visible after execution: rendered as a code element within a pre element
elif cell.get('cell_type').startswith('markdown') and cell.get('source').find('```') >= 0:
source = cell.get('source')
offset = source.find('```')
lines = source[offset + len('```'):].splitlines()
for line in lines:
if line.startswith('```'):
break
# Trying to go out of the current directory leads to
# trouble when deploying
if line.find('../') < 0 and not line.startswith('#'):
referenced.append(line)
# Clean out blank references
return [ref for ref in referenced if ref.strip()]
def expand_references(root_path, references):
"""Expands a set of reference patterns by evaluating them against the
given root directory. Expansions are performed against patterns
expressed in the same manner as in gitignore
(https://git-scm.com/docs/gitignore).
NOTE: Temporarily changes the current working directory when called.
Parameters
----------
root_path: str
Assumed root directory for the patterns
references: list
Reference patterns from get_reference_patterns expressed with
forward-slash directory separators
Returns
-------
list
Filename strings relative to the root path
"""
# Use normpath to convert to platform specific slashes, but be sure
# to retain a trailing slash which normpath pulls off
normalized_references = []
for ref in references:
normalized_ref = os.path.normpath(ref)
# un-normalized separator
if ref.endswith('/'):
normalized_ref += os.sep
normalized_references.append(normalized_ref)
references = normalized_references
globbed = []
negations = []
must_walk = []
for pattern in references:
if pattern and pattern.find(os.sep) < 0:
# simple shell glob
cwd = os.getcwd()
os.chdir(root_path)
if pattern.startswith('!'):
negations = negations + glob.glob(pattern[1:])
else:
globbed = globbed + glob.glob(pattern)
os.chdir(cwd)
elif pattern:
must_walk.append(pattern)
for pattern in must_walk:
pattern_is_negation = pattern.startswith('!')
if pattern_is_negation:
testpattern = pattern[1:]
else:
testpattern = pattern
for root, _, filenames in os.walk(root_path):
for filename in filenames:
joined = os.path.join(root[len(root_path) + 1:], filename)
if testpattern.endswith(os.sep):
if joined.startswith(testpattern):
if pattern_is_negation:
negations.append(joined)
else:
globbed.append(joined)
elif testpattern.find('**') >= 0:
# path wildcard
ends = testpattern.split('**')
if len(ends) == 2:
if joined.startswith(ends[0]) and joined.endswith(ends[1]):
if pattern_is_negation:
negations.append(joined)
else:
globbed.append(joined)
else:
# segments should be respected
if fnmatch.fnmatch(joined, testpattern):
if pattern_is_negation:
negations.append(joined)
else:
globbed.append(joined)
for negated in negations:
try:
globbed.remove(negated)
except ValueError as err:
pass
return set(globbed)
def copy_filelist(src, dst, src_relative_filenames):
"""Copies the given list of files, relative to src, into dst, creating
directories along the way as needed and ignore existence errors.
Skips any files that do not exist. Does not create empty directories
from src in dst.
Parameters
----------
src: str
Root of the source directory
dst: str
Root of the destination directory
src_relative_filenames: list
Filenames relative to src
"""
for filename in src_relative_filenames:
# Only consider the file if it exists in src
if os.path.isfile(os.path.join(src, filename)):
parent_relative = os.path.dirname(filename)
if parent_relative:
# Make sure the parent directory exists
parent_dst = os.path.join(dst, parent_relative)
try:
os.makedirs(parent_dst)
except OSError as exc:
if exc.errno == errno.EEXIST:
pass
else:
raise exc
shutil.copy2(os.path.join(src, filename), os.path.join(dst, filename))

@ -1,59 +0,0 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import os
import io
import zipfile
import notebook.bundler.tools as tools
def _jupyter_bundlerextension_paths():
"""Metadata for notebook bundlerextension"""
return [{
'name': 'notebook_zip_download',
'label': 'IPython Notebook bundle (.zip)',
'module_name': 'notebook.bundler.zip_bundler',
'group': 'download'
}]
def bundle(handler, model):
"""Create a zip file containing the original notebook and files referenced
from it. Retain the referenced files in paths relative to the notebook.
Return the zip as a file download.
Assumes the notebook and other files are all on local disk.
Parameters
----------
handler : tornado.web.RequestHandler
Handler that serviced the bundle request
model : dict
Notebook model from the configured ContentManager
"""
abs_nb_path = os.path.join(handler.settings['contents_manager'].root_dir,
model['path'])
notebook_filename = model['name']
notebook_name = os.path.splitext(notebook_filename)[0]
# Headers
zip_filename = os.path.splitext(notebook_name)[0] + '.zip'
handler.set_attachment_header(zip_filename)
handler.set_header('Content-Type', 'application/zip')
# Get associated files
ref_filenames = tools.get_file_references(abs_nb_path, 4)
# Prepare the zip file
zip_buffer = io.BytesIO()
zipf = zipfile.ZipFile(zip_buffer, mode='w', compression=zipfile.ZIP_DEFLATED)
zipf.write(abs_nb_path, notebook_filename)
notebook_dir = os.path.dirname(abs_nb_path)
for nb_relative_filename in ref_filenames:
# Build absolute path to file on disk
abs_fn = os.path.join(notebook_dir, nb_relative_filename)
# Store file under path relative to notebook
zipf.write(abs_fn, nb_relative_filename)
zipf.close()
# Return the buffer value as the response
handler.finish(zip_buffer.getvalue())

@ -1,132 +0,0 @@
"""Manager to read and modify config data in JSON files."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import errno
import glob
import io
import json
import os
import copy
from traitlets.config import LoggingConfigurable
from traitlets.traitlets import Unicode, Bool
def recursive_update(target, new):
"""Recursively update one dictionary using another.
None values will delete their keys.
"""
for k, v in new.items():
if isinstance(v, dict):
if k not in target:
target[k] = {}
recursive_update(target[k], v)
if not target[k]:
# Prune empty subdicts
del target[k]
elif v is None:
target.pop(k, None)
else:
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.
"""
config_dir = Unicode('.')
read_directory = Bool(True)
def ensure_config_dir_exists(self):
"""Will try to create the config_dir directory."""
try:
os.makedirs(self.config_dir, 0o755)
except OSError as e:
if e.errno != errno.EEXIST:
raise
def file_name(self, section_name):
"""Returns the json filename for the section_name: {config_dir}/{section_name}.json"""
return os.path.join(self.config_dir, section_name+'.json')
def directory(self, section_name):
"""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, 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)] 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
# .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))
data = {}
for path in paths:
if os.path.isfile(path):
with io.open(path, encoding='utf-8') as f:
recursive_update(data, json.load(f))
return data
def set(self, section_name, data):
"""Store the given config data.
"""
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)
f = io.open(filename, 'w', encoding='utf-8')
with f:
f.write(json_content)
def update(self, section_name, new_data):
"""Modify the config section by recursively updating it with new_data.
Returns the modified config data as a dictionary.
"""
data = self.get(section_name)
recursive_update(data, new_data)
self.set(section_name, data)
return data

@ -1,28 +0,0 @@
"""Tornado handlers for the terminal emulator."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from tornado import web
from ..base.handlers import IPythonHandler, path_regex
from ..utils import url_escape
class EditorHandler(IPythonHandler):
"""Render the text editor interface."""
@web.authenticated
def get(self, path):
path = path.strip('/')
if not self.contents_manager.file_exists(path):
raise web.HTTPError(404, u'File does not exist: %s' % path)
basename = path.rsplit('/', 1)[-1]
self.write(self.render_template('edit.html',
file_path=url_escape(path),
basename=basename,
page_title=basename + " (editing)",
)
)
default_handlers = [
(r"/edit%s" % path_regex, EditorHandler),
]

@ -1,103 +0,0 @@
"""Utilities for installing extensions"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import os
from tornado.log import LogFormatter
from traitlets import Bool, Any
from jupyter_core.application import JupyterApp
from jupyter_core.paths import (
jupyter_config_dir, ENV_CONFIG_PATH, SYSTEM_CONFIG_PATH
)
from ._version import __version__
class ArgumentConflict(ValueError):
pass
_base_flags = {}
_base_flags.update(JupyterApp.flags)
_base_flags.pop("y", None)
_base_flags.pop("generate-config", None)
_base_flags.update({
"user" : ({
"BaseExtensionApp" : {
"user" : True,
}}, "Apply the operation only for the given user"
),
"system" : ({
"BaseExtensionApp" : {
"user" : False,
"sys_prefix": False,
}}, "Apply the operation system-wide"
),
"sys-prefix" : ({
"BaseExtensionApp" : {
"sys_prefix" : True,
}}, "Use sys.prefix as the prefix for installing nbextensions (for environments, packaging)"
),
"py" : ({
"BaseExtensionApp" : {
"python" : True,
}}, "Install from a Python package"
)
})
_base_flags['python'] = _base_flags['py']
_base_aliases = {}
_base_aliases.update(JupyterApp.aliases)
class BaseExtensionApp(JupyterApp):
"""Base nbextension installer app"""
_log_formatter_cls = LogFormatter
flags = _base_flags
aliases = _base_aliases
version = __version__
user = Bool(False, config=True, help="Whether to do a user install")
sys_prefix = Bool(False, config=True, help="Use the sys.prefix as the prefix")
python = Bool(False, config=True, help="Install from a Python package")
# Remove for 5.0...
verbose = Any(None, config=True, help="DEPRECATED: Verbosity level")
def _verbose_changed(self):
"""Warn about verbosity changes"""
import warnings
warnings.warn("`verbose` traits of `{}` has been deprecated, has no effects and will be removed in notebook 5.0.".format(type(self).__name__), DeprecationWarning)
def _log_format_default(self):
"""A default format for messages"""
return "%(message)s"
def _get_config_dir(user=False, sys_prefix=False):
"""Get the location of config files for the current context
Returns the string to the environment
Parameters
----------
user : bool [default: False]
Get the user's .jupyter config directory
sys_prefix : bool [default: False]
Get sys.prefix, i.e. ~/.envs/my-env/etc/jupyter
"""
user = False if sys_prefix else user
if user and sys_prefix:
raise ArgumentConflict("Cannot specify more than one of user or sys_prefix")
if user:
nbext = jupyter_config_dir()
elif sys_prefix:
nbext = ENV_CONFIG_PATH[0]
else:
nbext = SYSTEM_CONFIG_PATH[0]
return nbext
# Constants for pretty print extension listing function.
# Window doesn't support coloring in the commandline
GREEN_ENABLED = '\033[32m enabled \033[0m' if os.name != 'nt' else 'enabled '
RED_DISABLED = '\033[31mdisabled\033[0m' if os.name != 'nt' else 'disabled'
GREEN_OK = '\033[32mOK\033[0m' if os.name != 'nt' else 'ok'
RED_X = '\033[31m X\033[0m' if os.name != 'nt' else ' X'

@ -1,84 +0,0 @@
"""Serve files directly from the ContentsManager."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import mimetypes
import json
from base64 import decodebytes
from tornado import gen, web
from notebook.base.handlers import IPythonHandler
from notebook.utils import maybe_future
class FilesHandler(IPythonHandler):
"""serve files via ContentsManager
Normally used when ContentsManager is not a FileContentsManager.
FileContentsManager subclasses use AuthenticatedFilesHandler by default,
a subclass of StaticFileHandler.
"""
@property
def content_security_policy(self):
# In case we're serving HTML/SVG, confine any Javascript to a unique
# origin so it can't interact with the notebook server.
return super().content_security_policy + "; sandbox allow-scripts"
@web.authenticated
def head(self, path):
self.check_xsrf_cookie()
return self.get(path, include_body=False)
@web.authenticated
@gen.coroutine
def get(self, path, include_body=True):
# /files/ requests must originate from the same site
self.check_xsrf_cookie()
cm = self.contents_manager
if cm.is_hidden(path) and not cm.allow_hidden:
self.log.info("Refusing to serve hidden file, via 404 Error")
raise web.HTTPError(404)
path = path.strip('/')
if '/' in path:
_, name = path.rsplit('/', 1)
else:
name = path
model = yield maybe_future(cm.get(path, type='file', content=include_body))
if self.get_argument("download", False):
self.set_attachment_header(name)
# get mimetype from filename
if name.lower().endswith('.ipynb'):
self.set_header('Content-Type', 'application/x-ipynb+json')
else:
cur_mime = mimetypes.guess_type(name)[0]
if cur_mime == 'text/plain':
self.set_header('Content-Type', 'text/plain; charset=UTF-8')
elif cur_mime is not None:
self.set_header('Content-Type', cur_mime)
else:
if model['format'] == 'base64':
self.set_header('Content-Type', 'application/octet-stream')
else:
self.set_header('Content-Type', 'text/plain; charset=UTF-8')
if include_body:
if model['format'] == 'base64':
b64_bytes = model['content'].encode('ascii')
self.write(decodebytes(b64_bytes))
elif model['format'] == 'json':
self.write(json.dumps(model['content']))
else:
self.write(model['content'])
self.flush()
default_handlers = []

@ -1,263 +0,0 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import os
import logging
import mimetypes
import random
from ..base.handlers import APIHandler, IPythonHandler
from ..utils import url_path_join
from tornado import gen, web
from tornado.concurrent import Future
from tornado.ioloop import IOLoop, PeriodicCallback
from tornado.websocket import WebSocketHandler, websocket_connect
from tornado.httpclient import HTTPRequest
from tornado.escape import url_escape, json_decode, utf8
from ipython_genutils.py3compat import cast_unicode
from jupyter_client.session import Session
from traitlets.config.configurable import LoggingConfigurable
from .managers import GatewayClient
# Keepalive ping interval (default: 30 seconds)
GATEWAY_WS_PING_INTERVAL_SECS = int(os.getenv('GATEWAY_WS_PING_INTERVAL_SECS', 30))
class WebSocketChannelsHandler(WebSocketHandler, IPythonHandler):
session = None
gateway = None
kernel_id = None
ping_callback = None
def check_origin(self, origin=None):
return IPythonHandler.check_origin(self, origin)
def set_default_headers(self):
"""Undo the set_default_headers in IPythonHandler which doesn't make sense for websockets"""
pass
def get_compression_options(self):
# use deflate compress websocket
return {}
def authenticate(self):
"""Run before finishing the GET request
Extend this method to add logic that should fire before
the websocket finishes completing.
"""
# authenticate the request before opening the websocket
if self.get_current_user() is None:
self.log.warning("Couldn't authenticate WebSocket connection")
raise web.HTTPError(403)
if self.get_argument('session_id', False):
self.session.session = cast_unicode(self.get_argument('session_id'))
else:
self.log.warning("No session ID specified")
def initialize(self):
self.log.debug("Initializing websocket connection %s", self.request.path)
self.session = Session(config=self.config)
self.gateway = GatewayWebSocketClient(gateway_url=GatewayClient.instance().url)
@gen.coroutine
def get(self, kernel_id, *args, **kwargs):
self.authenticate()
self.kernel_id = cast_unicode(kernel_id, 'ascii')
yield super().get(kernel_id=kernel_id, *args, **kwargs)
def send_ping(self):
if self.ws_connection is None and self.ping_callback is not None:
self.ping_callback.stop()
return
self.ping(b'')
def open(self, kernel_id, *args, **kwargs):
"""Handle web socket connection open to notebook server and delegate to gateway web socket handler """
self.ping_callback = PeriodicCallback(self.send_ping, GATEWAY_WS_PING_INTERVAL_SECS * 1000)
self.ping_callback.start()
self.gateway.on_open(
kernel_id=kernel_id,
message_callback=self.write_message,
compression_options=self.get_compression_options()
)
def on_message(self, message):
"""Forward message to gateway web socket handler."""
self.gateway.on_message(message)
def write_message(self, message, binary=False):
"""Send message back to notebook client. This is called via callback from self.gateway._read_messages."""
if self.ws_connection: # prevent WebSocketClosedError
if isinstance(message, bytes):
binary = True
super().write_message(message, binary=binary)
elif self.log.isEnabledFor(logging.DEBUG):
msg_summary = WebSocketChannelsHandler._get_message_summary(json_decode(utf8(message)))
self.log.debug("Notebook client closed websocket connection - message dropped: {}".format(msg_summary))
def on_close(self):
self.log.debug("Closing websocket connection %s", self.request.path)
self.gateway.on_close()
super().on_close()
@staticmethod
def _get_message_summary(message):
summary = []
message_type = message['msg_type']
summary.append('type: {}'.format(message_type))
if message_type == 'status':
summary.append(', state: {}'.format(message['content']['execution_state']))
elif message_type == 'error':
summary.append(', {}:{}:{}'.format(message['content']['ename'],
message['content']['evalue'],
message['content']['traceback']))
else:
summary.append(', ...') # don't display potentially sensitive data
return ''.join(summary)
class GatewayWebSocketClient(LoggingConfigurable):
"""Proxy web socket connection to a kernel/enterprise gateway."""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.kernel_id = None
self.ws = None
self.ws_future = Future()
self.disconnected = False
self.retry = 0
@gen.coroutine
def _connect(self, kernel_id):
# websocket is initialized before connection
self.ws = None
self.kernel_id = kernel_id
ws_url = url_path_join(
GatewayClient.instance().ws_url,
GatewayClient.instance().kernels_endpoint, url_escape(kernel_id), 'channels'
)
self.log.info('Connecting to {}'.format(ws_url))
kwargs = {}
kwargs = GatewayClient.instance().load_connection_args(**kwargs)
request = HTTPRequest(ws_url, **kwargs)
self.ws_future = websocket_connect(request)
self.ws_future.add_done_callback(self._connection_done)
def _connection_done(self, fut):
if not self.disconnected and fut.exception() is None: # prevent concurrent.futures._base.CancelledError
self.ws = fut.result()
self.retry = 0
self.log.debug("Connection is ready: ws: {}".format(self.ws))
else:
self.log.warning("Websocket connection has been closed via client disconnect or due to error. "
"Kernel with ID '{}' may not be terminated on GatewayClient: {}".
format(self.kernel_id, GatewayClient.instance().url))
def _disconnect(self):
self.disconnected = True
if self.ws is not None:
# Close connection
self.ws.close()
elif not self.ws_future.done():
# Cancel pending connection. Since future.cancel() is a noop on tornado, we'll track cancellation locally
self.ws_future.cancel()
self.log.debug("_disconnect: future cancelled, disconnected: {}".format(self.disconnected))
@gen.coroutine
def _read_messages(self, callback):
"""Read messages from gateway server."""
while self.ws is not None:
message = None
if not self.disconnected:
try:
message = yield self.ws.read_message()
except Exception as e:
self.log.error("Exception reading message from websocket: {}".format(e)) # , exc_info=True)
if message is None:
if not self.disconnected:
self.log.warning("Lost connection to Gateway: {}".format(self.kernel_id))
break
callback(message) # pass back to notebook client (see self.on_open and WebSocketChannelsHandler.open)
else: # ws cancelled - stop reading
break
# NOTE(esevan): if websocket is not disconnected by client, try to reconnect.
if not self.disconnected and self.retry < GatewayClient.instance().gateway_retry_max:
jitter = random.randint(10, 100) * 0.01
retry_interval = min(GatewayClient.instance().gateway_retry_interval * (2 ** self.retry),
GatewayClient.instance().gateway_retry_interval_max) + jitter
self.retry += 1
self.log.info("Attempting to re-establish the connection to Gateway in %s secs (%s/%s): %s",
retry_interval, self.retry, GatewayClient.instance().gateway_retry_max, self.kernel_id)
yield gen.sleep(retry_interval)
self._connect(self.kernel_id)
loop = IOLoop.current()
loop.add_future(self.ws_future, lambda future: self._read_messages(callback))
def on_open(self, kernel_id, message_callback, **kwargs):
"""Web socket connection open against gateway server."""
self._connect(kernel_id)
loop = IOLoop.current()
loop.add_future(
self.ws_future,
lambda future: self._read_messages(message_callback)
)
def on_message(self, message):
"""Send message to gateway server."""
if self.ws is None:
loop = IOLoop.current()
loop.add_future(
self.ws_future,
lambda future: self._write_message(message)
)
else:
self._write_message(message)
def _write_message(self, message):
"""Send message to gateway server."""
try:
if not self.disconnected and self.ws is not None:
self.ws.write_message(message)
except Exception as e:
self.log.error("Exception writing message to websocket: {}".format(e)) # , exc_info=True)
def on_close(self):
"""Web socket closed event."""
self._disconnect()
class GatewayResourceHandler(APIHandler):
"""Retrieves resources for specific kernelspec definitions from kernel/enterprise gateway."""
@web.authenticated
@gen.coroutine
def get(self, kernel_name, path, include_body=True):
ksm = self.kernel_spec_manager
kernel_spec_res = yield ksm.get_kernel_spec_resource(kernel_name, path)
if kernel_spec_res is None:
self.log.warning("Kernelspec resource '{}' for '{}' not found. Gateway may not support"
" resource serving.".format(path, kernel_name))
else:
self.set_header("Content-Type", mimetypes.guess_type(path)[0])
self.finish(kernel_spec_res)
from ..services.kernels.handlers import _kernel_id_regex
from ..services.kernelspecs.handlers import kernel_name_regex
default_handlers = [
(r"/api/kernels/%s/channels" % _kernel_id_regex, WebSocketChannelsHandler),
(r"/kernelspecs/%s/(?P<path>.*)" % kernel_name_regex, GatewayResourceHandler),
]

@ -1,662 +0,0 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import os
import json
from socket import gaierror
from tornado import web
from tornado.escape import json_encode, json_decode, url_escape
from tornado.httpclient import HTTPClient, AsyncHTTPClient, HTTPError
from ..services.kernels.kernelmanager import AsyncMappingKernelManager
from ..services.sessions.sessionmanager import SessionManager
from jupyter_client.kernelspec import KernelSpecManager
from ..utils import url_path_join
from traitlets import Instance, Unicode, Int, Float, Bool, default, validate, TraitError
from traitlets.config import SingletonConfigurable
class GatewayClient(SingletonConfigurable):
"""This class manages the configuration. It's its own singleton class so that we
can share these values across all objects. It also contains some helper methods
to build request arguments out of the various config options.
"""
url = Unicode(default_value=None, allow_none=True, config=True,
help="""The url of the Kernel or Enterprise Gateway server where
kernel specifications are defined and kernel management takes place.
If defined, this Notebook server acts as a proxy for all kernel
management and kernel specification retrieval. (JUPYTER_GATEWAY_URL env var)
"""
)
url_env = 'JUPYTER_GATEWAY_URL'
@default('url')
def _url_default(self):
return os.environ.get(self.url_env)
@validate('url')
def _url_validate(self, proposal):
value = proposal['value']
# Ensure value, if present, starts with 'http'
if value is not None and len(value) > 0:
if not str(value).lower().startswith('http'):
raise TraitError("GatewayClient url must start with 'http': '%r'" % value)
return value
ws_url = Unicode(default_value=None, allow_none=True, config=True,
help="""The websocket url of the Kernel or Enterprise Gateway server. If not provided, this value
will correspond to the value of the Gateway url with 'ws' in place of 'http'. (JUPYTER_GATEWAY_WS_URL env var)
"""
)
ws_url_env = 'JUPYTER_GATEWAY_WS_URL'
@default('ws_url')
def _ws_url_default(self):
default_value = os.environ.get(self.ws_url_env)
if default_value is None:
if self.gateway_enabled:
default_value = self.url.lower().replace('http', 'ws')
return default_value
@validate('ws_url')
def _ws_url_validate(self, proposal):
value = proposal['value']
# Ensure value, if present, starts with 'ws'
if value is not None and len(value) > 0:
if not str(value).lower().startswith('ws'):
raise TraitError("GatewayClient ws_url must start with 'ws': '%r'" % value)
return value
kernels_endpoint_default_value = '/api/kernels'
kernels_endpoint_env = 'JUPYTER_GATEWAY_KERNELS_ENDPOINT'
kernels_endpoint = Unicode(default_value=kernels_endpoint_default_value, config=True,
help="""The gateway API endpoint for accessing kernel resources (JUPYTER_GATEWAY_KERNELS_ENDPOINT env var)""")
@default('kernels_endpoint')
def _kernels_endpoint_default(self):
return os.environ.get(self.kernels_endpoint_env, self.kernels_endpoint_default_value)
kernelspecs_endpoint_default_value = '/api/kernelspecs'
kernelspecs_endpoint_env = 'JUPYTER_GATEWAY_KERNELSPECS_ENDPOINT'
kernelspecs_endpoint = Unicode(default_value=kernelspecs_endpoint_default_value, config=True,
help="""The gateway API endpoint for accessing kernelspecs (JUPYTER_GATEWAY_KERNELSPECS_ENDPOINT env var)""")
@default('kernelspecs_endpoint')
def _kernelspecs_endpoint_default(self):
return os.environ.get(self.kernelspecs_endpoint_env, self.kernelspecs_endpoint_default_value)
kernelspecs_resource_endpoint_default_value = '/kernelspecs'
kernelspecs_resource_endpoint_env = 'JUPYTER_GATEWAY_KERNELSPECS_RESOURCE_ENDPOINT'
kernelspecs_resource_endpoint = Unicode(default_value=kernelspecs_resource_endpoint_default_value, config=True,
help="""The gateway endpoint for accessing kernelspecs resources
(JUPYTER_GATEWAY_KERNELSPECS_RESOURCE_ENDPOINT env var)""")
@default('kernelspecs_resource_endpoint')
def _kernelspecs_resource_endpoint_default(self):
return os.environ.get(self.kernelspecs_resource_endpoint_env, self.kernelspecs_resource_endpoint_default_value)
connect_timeout_default_value = 40.0
connect_timeout_env = 'JUPYTER_GATEWAY_CONNECT_TIMEOUT'
connect_timeout = Float(default_value=connect_timeout_default_value, config=True,
help="""The time allowed for HTTP connection establishment with the Gateway server.
(JUPYTER_GATEWAY_CONNECT_TIMEOUT env var)""")
@default('connect_timeout')
def connect_timeout_default(self):
return float(os.environ.get('JUPYTER_GATEWAY_CONNECT_TIMEOUT', self.connect_timeout_default_value))
request_timeout_default_value = 40.0
request_timeout_env = 'JUPYTER_GATEWAY_REQUEST_TIMEOUT'
request_timeout = Float(default_value=request_timeout_default_value, config=True,
help="""The time allowed for HTTP request completion. (JUPYTER_GATEWAY_REQUEST_TIMEOUT env var)""")
@default('request_timeout')
def request_timeout_default(self):
return float(os.environ.get('JUPYTER_GATEWAY_REQUEST_TIMEOUT', self.request_timeout_default_value))
client_key = Unicode(default_value=None, allow_none=True, config=True,
help="""The filename for client SSL key, if any. (JUPYTER_GATEWAY_CLIENT_KEY env var)
"""
)
client_key_env = 'JUPYTER_GATEWAY_CLIENT_KEY'
@default('client_key')
def _client_key_default(self):
return os.environ.get(self.client_key_env)
client_cert = Unicode(default_value=None, allow_none=True, config=True,
help="""The filename for client SSL certificate, if any. (JUPYTER_GATEWAY_CLIENT_CERT env var)
"""
)
client_cert_env = 'JUPYTER_GATEWAY_CLIENT_CERT'
@default('client_cert')
def _client_cert_default(self):
return os.environ.get(self.client_cert_env)
ca_certs = Unicode(default_value=None, allow_none=True, config=True,
help="""The filename of CA certificates or None to use defaults. (JUPYTER_GATEWAY_CA_CERTS env var)
"""
)
ca_certs_env = 'JUPYTER_GATEWAY_CA_CERTS'
@default('ca_certs')
def _ca_certs_default(self):
return os.environ.get(self.ca_certs_env)
http_user = Unicode(default_value=None, allow_none=True, config=True,
help="""The username for HTTP authentication. (JUPYTER_GATEWAY_HTTP_USER env var)
"""
)
http_user_env = 'JUPYTER_GATEWAY_HTTP_USER'
@default('http_user')
def _http_user_default(self):
return os.environ.get(self.http_user_env)
http_pwd = Unicode(default_value=None, allow_none=True, config=True,
help="""The password for HTTP authentication. (JUPYTER_GATEWAY_HTTP_PWD env var)
"""
)
http_pwd_env = 'JUPYTER_GATEWAY_HTTP_PWD'
@default('http_pwd')
def _http_pwd_default(self):
return os.environ.get(self.http_pwd_env)
headers_default_value = '{}'
headers_env = 'JUPYTER_GATEWAY_HEADERS'
headers = Unicode(default_value=headers_default_value, allow_none=True, config=True,
help="""Additional HTTP headers to pass on the request. This value will be converted to a dict.
(JUPYTER_GATEWAY_HEADERS env var)
"""
)
@default('headers')
def _headers_default(self):
return os.environ.get(self.headers_env, self.headers_default_value)
auth_token = Unicode(default_value=None, allow_none=True, config=True,
help="""The authorization token used in the HTTP headers. (JUPYTER_GATEWAY_AUTH_TOKEN env var)
"""
)
auth_token_env = 'JUPYTER_GATEWAY_AUTH_TOKEN'
@default('auth_token')
def _auth_token_default(self):
return os.environ.get(self.auth_token_env, '')
validate_cert_default_value = True
validate_cert_env = 'JUPYTER_GATEWAY_VALIDATE_CERT'
validate_cert = Bool(default_value=validate_cert_default_value, config=True,
help="""For HTTPS requests, determines if server's certificate should be validated or not.
(JUPYTER_GATEWAY_VALIDATE_CERT env var)"""
)
@default('validate_cert')
def validate_cert_default(self):
return bool(os.environ.get(self.validate_cert_env, str(self.validate_cert_default_value)) not in ['no', 'false'])
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._static_args = {} # initialized on first use
env_whitelist_default_value = ''
env_whitelist_env = 'JUPYTER_GATEWAY_ENV_WHITELIST'
env_whitelist = Unicode(default_value=env_whitelist_default_value, config=True,
help="""A comma-separated list of environment variable names that will be included, along with
their values, in the kernel startup request. The corresponding `env_whitelist` configuration
value must also be set on the Gateway server - since that configuration value indicates which
environmental values to make available to the kernel. (JUPYTER_GATEWAY_ENV_WHITELIST env var)""")
@default('env_whitelist')
def _env_whitelist_default(self):
return os.environ.get(self.env_whitelist_env, self.env_whitelist_default_value)
gateway_retry_interval_default_value = 1.0
gateway_retry_interval_env = 'JUPYTER_GATEWAY_RETRY_INTERVAL'
gateway_retry_interval = Float(default_value=gateway_retry_interval_default_value, config=True,
help="""The time allowed for HTTP reconnection with the Gateway server for the first time.
Next will be JUPYTER_GATEWAY_RETRY_INTERVAL multiplied by two in factor of numbers of retries
but less than JUPYTER_GATEWAY_RETRY_INTERVAL_MAX.
(JUPYTER_GATEWAY_RETRY_INTERVAL env var)""")
@default('gateway_retry_interval')
def gateway_retry_interval_default(self):
return float(os.environ.get('JUPYTER_GATEWAY_RETRY_INTERVAL', self.gateway_retry_interval_default_value))
gateway_retry_interval_max_default_value = 30.0
gateway_retry_interval_max_env = 'JUPYTER_GATEWAY_RETRY_INTERVAL_MAX'
gateway_retry_interval_max = Float(default_value=gateway_retry_interval_max_default_value, config=True,
help="""The maximum time allowed for HTTP reconnection retry with the Gateway server.
(JUPYTER_GATEWAY_RETRY_INTERVAL_MAX env var)""")
@default('gateway_retry_interval_max')
def gateway_retry_interval_max_default(self):
return float(os.environ.get('JUPYTER_GATEWAY_RETRY_INTERVAL_MAX', self.gateway_retry_interval_max_default_value))
gateway_retry_max_default_value = 5
gateway_retry_max_env = 'JUPYTER_GATEWAY_RETRY_MAX'
gateway_retry_max = Int(default_value=gateway_retry_max_default_value, config=True,
help="""The maximum retries allowed for HTTP reconnection with the Gateway server.
(JUPYTER_GATEWAY_RETRY_MAX env var)""")
@default('gateway_retry_max')
def gateway_retry_max_default(self):
return int(os.environ.get('JUPYTER_GATEWAY_RETRY_MAX', self.gateway_retry_max_default_value))
@property
def gateway_enabled(self):
return bool(self.url is not None and len(self.url) > 0)
# Ensure KERNEL_LAUNCH_TIMEOUT has a default value.
KERNEL_LAUNCH_TIMEOUT = int(os.environ.get('KERNEL_LAUNCH_TIMEOUT', 40))
def init_static_args(self):
"""Initialize arguments used on every request. Since these are static values, we'll
perform this operation once.
"""
# Ensure that request timeout and KERNEL_LAUNCH_TIMEOUT are the same, taking the
# greater value of the two.
if self.request_timeout < float(GatewayClient.KERNEL_LAUNCH_TIMEOUT):
self.request_timeout = float(GatewayClient.KERNEL_LAUNCH_TIMEOUT)
elif self.request_timeout > float(GatewayClient.KERNEL_LAUNCH_TIMEOUT):
GatewayClient.KERNEL_LAUNCH_TIMEOUT = int(self.request_timeout)
# Ensure any adjustments are reflected in env.
os.environ['KERNEL_LAUNCH_TIMEOUT'] = str(GatewayClient.KERNEL_LAUNCH_TIMEOUT)
self._static_args['headers'] = json.loads(self.headers)
if 'Authorization' not in self._static_args['headers'].keys():
self._static_args['headers'].update({
'Authorization': 'token {}'.format(self.auth_token)
})
self._static_args['connect_timeout'] = self.connect_timeout
self._static_args['request_timeout'] = self.request_timeout
self._static_args['validate_cert'] = self.validate_cert
if self.client_cert:
self._static_args['client_cert'] = self.client_cert
self._static_args['client_key'] = self.client_key
if self.ca_certs:
self._static_args['ca_certs'] = self.ca_certs
if self.http_user:
self._static_args['auth_username'] = self.http_user
if self.http_pwd:
self._static_args['auth_password'] = self.http_pwd
def load_connection_args(self, **kwargs):
"""Merges the static args relative to the connection, with the given keyword arguments. If statics
have yet to be initialized, we'll do that here.
"""
if len(self._static_args) == 0:
self.init_static_args()
for arg, static_value in self._static_args.items():
if arg == 'headers':
given_value = kwargs.setdefault(arg, {})
if isinstance(given_value, dict):
given_value.update(static_value)
else:
kwargs[arg] = static_value
return kwargs
async def gateway_request(endpoint, **kwargs):
"""Make an async request to kernel gateway endpoint, returns a response """
client = AsyncHTTPClient()
kwargs = GatewayClient.instance().load_connection_args(**kwargs)
try:
response = await client.fetch(endpoint, **kwargs)
# Trap a set of common exceptions so that we can inform the user that their Gateway url is incorrect
# or the server is not running.
# NOTE: We do this here since this handler is called during the Notebook's startup and subsequent refreshes
# of the tree view.
except ConnectionRefusedError as e:
raise web.HTTPError(
503,
"Connection refused from Gateway server url '{}'. Check to be sure the"
" Gateway instance is running.".format(GatewayClient.instance().url)
) from e
except HTTPError as e:
# This can occur if the host is valid (e.g., foo.com) but there's nothing there.
raise web.HTTPError(e.code, "Error attempting to connect to Gateway server url '{}'. "
"Ensure gateway url is valid and the Gateway instance is running.".
format(GatewayClient.instance().url)) from e
except gaierror as e:
raise web.HTTPError(
404,
"The Gateway server specified in the gateway_url '{}' doesn't appear to be valid. Ensure gateway "
"url is valid and the Gateway instance is running.".format(GatewayClient.instance().url)
) from e
return response
class GatewayKernelManager(AsyncMappingKernelManager):
"""Kernel manager that supports remote kernels hosted by Jupyter Kernel or Enterprise Gateway."""
# We'll maintain our own set of kernel ids
_kernels = {}
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.base_endpoint = url_path_join(GatewayClient.instance().url, GatewayClient.instance().kernels_endpoint)
def __contains__(self, kernel_id):
return kernel_id in self._kernels
def remove_kernel(self, kernel_id):
"""Complete override since we want to be more tolerant of missing keys """
try:
return self._kernels.pop(kernel_id)
except KeyError:
pass
def _get_kernel_endpoint_url(self, kernel_id=None):
"""Builds a url for the kernels endpoint
Parameters
----------
kernel_id: kernel UUID (optional)
"""
if kernel_id:
return url_path_join(self.base_endpoint, url_escape(str(kernel_id)))
return self.base_endpoint
async def start_kernel(self, kernel_id=None, path=None, **kwargs):
"""Start a kernel for a session and return its kernel_id.
Parameters
----------
kernel_id : uuid
The uuid to associate the new kernel with. If this
is not None, this kernel will be persistent whenever it is
requested.
path : API path
The API path (unicode, '/' delimited) for the cwd.
Will be transformed to an OS path relative to root_dir.
"""
self.log.info('Request start kernel: kernel_id=%s, path="%s"', kernel_id, path)
if kernel_id is None:
if path is not None:
kwargs['cwd'] = self.cwd_for_path(path)
kernel_name = kwargs.get('kernel_name', 'python3')
kernel_url = self._get_kernel_endpoint_url()
self.log.debug("Request new kernel at: %s" % kernel_url)
# Let KERNEL_USERNAME take precedent over http_user config option.
if os.environ.get('KERNEL_USERNAME') is None and GatewayClient.instance().http_user:
os.environ['KERNEL_USERNAME'] = GatewayClient.instance().http_user
kernel_env = {k: v for (k, v) in dict(os.environ).items() if k.startswith('KERNEL_')
or k in GatewayClient.instance().env_whitelist.split(",")}
# Convey the full path to where this notebook file is located.
if path is not None and kernel_env.get('KERNEL_WORKING_DIR') is None:
kernel_env['KERNEL_WORKING_DIR'] = kwargs['cwd']
json_body = json_encode({'name': kernel_name, 'env': kernel_env})
response = await gateway_request(
kernel_url, method='POST', headers={'Content-Type': 'application/json'}, body=json_body
)
kernel = json_decode(response.body)
kernel_id = kernel['id']
self.log.info("Kernel started: %s" % kernel_id)
self.log.debug("Kernel args: %r" % kwargs)
else:
kernel = await self.get_kernel(kernel_id)
kernel_id = kernel['id']
self.log.info("Using existing kernel: %s" % kernel_id)
self._kernels[kernel_id] = kernel
return kernel_id
async def get_kernel(self, kernel_id=None, **kwargs):
"""Get kernel for kernel_id.
Parameters
----------
kernel_id : uuid
The uuid of the kernel.
"""
kernel_url = self._get_kernel_endpoint_url(kernel_id)
self.log.debug("Request kernel at: %s" % kernel_url)
try:
response = await gateway_request(kernel_url, method='GET')
except web.HTTPError as error:
if error.status_code == 404:
self.log.warn("Kernel not found at: %s" % kernel_url)
self.remove_kernel(kernel_id)
kernel = None
else:
raise
else:
kernel = json_decode(response.body)
# Only update our models if we already know about this kernel
if kernel_id in self._kernels:
self._kernels[kernel_id] = kernel
self.log.debug("Kernel retrieved: %s", kernel)
else:
self.log.warning("Kernel '%s' is not managed by this instance.", kernel_id)
kernel = None
return kernel
async def kernel_model(self, kernel_id):
"""Return a dictionary of kernel information described in the
JSON standard model.
Parameters
----------
kernel_id : uuid
The uuid of the kernel.
"""
self.log.debug("RemoteKernelManager.kernel_model: %s", kernel_id)
model = await self.get_kernel(kernel_id)
return model
async def list_kernels(self, **kwargs):
"""Get a list of kernels."""
kernel_url = self._get_kernel_endpoint_url()
self.log.debug("Request list kernels: %s", kernel_url)
response = await gateway_request(kernel_url, method='GET')
kernels = json_decode(response.body)
# Only update our models if we already know about the kernels
self._kernels = {x['id']: x for x in kernels if x['id'] in self._kernels}
return list(self._kernels.values())
async def shutdown_kernel(self, kernel_id, now=False, restart=False):
"""Shutdown a kernel by its kernel uuid.
Parameters
==========
kernel_id : uuid
The id of the kernel to shutdown.
now : bool
Shutdown the kernel immediately (True) or gracefully (False)
restart : bool
The purpose of this shutdown is to restart the kernel (True)
"""
kernel_url = self._get_kernel_endpoint_url(kernel_id)
self.log.debug("Request shutdown kernel at: %s", kernel_url)
response = await gateway_request(kernel_url, method='DELETE')
self.log.debug("Shutdown kernel response: %d %s", response.code, response.reason)
self.remove_kernel(kernel_id)
async def restart_kernel(self, kernel_id, now=False, **kwargs):
"""Restart a kernel by its kernel uuid.
Parameters
==========
kernel_id : uuid
The id of the kernel to restart.
"""
kernel_url = self._get_kernel_endpoint_url(kernel_id) + '/restart'
self.log.debug("Request restart kernel at: %s", kernel_url)
response = await gateway_request(
kernel_url, method='POST', headers={'Content-Type': 'application/json'}, body=json_encode({})
)
self.log.debug("Restart kernel response: %d %s", response.code, response.reason)
async def interrupt_kernel(self, kernel_id, **kwargs):
"""Interrupt a kernel by its kernel uuid.
Parameters
==========
kernel_id : uuid
The id of the kernel to interrupt.
"""
kernel_url = self._get_kernel_endpoint_url(kernel_id) + '/interrupt'
self.log.debug("Request interrupt kernel at: %s", kernel_url)
response = await gateway_request(
kernel_url, method='POST', headers={'Content-Type': 'application/json'}, body=json_encode({})
)
self.log.debug("Interrupt kernel response: %d %s", response.code, response.reason)
def shutdown_all(self, now=False):
"""Shutdown all kernels."""
# Note: We have to make this sync because the NotebookApp does not wait for async.
shutdown_kernels = []
kwargs = {'method': 'DELETE'}
kwargs = GatewayClient.instance().load_connection_args(**kwargs)
client = HTTPClient()
for kernel_id in self._kernels:
kernel_url = self._get_kernel_endpoint_url(kernel_id)
self.log.debug("Request delete kernel at: %s", kernel_url)
try:
response = client.fetch(kernel_url, **kwargs)
except HTTPError:
pass
else:
self.log.debug("Delete kernel response: %d %s", response.code, response.reason)
shutdown_kernels.append(kernel_id) # avoid changing dict size during iteration
client.close()
for kernel_id in shutdown_kernels:
self.remove_kernel(kernel_id)
class GatewayKernelSpecManager(KernelSpecManager):
def __init__(self, **kwargs):
super().__init__(**kwargs)
base_endpoint = url_path_join(GatewayClient.instance().url,
GatewayClient.instance().kernelspecs_endpoint)
self.base_endpoint = GatewayKernelSpecManager._get_endpoint_for_user_filter(base_endpoint)
self.base_resource_endpoint = url_path_join(GatewayClient.instance().url,
GatewayClient.instance().kernelspecs_resource_endpoint)
@staticmethod
def _get_endpoint_for_user_filter(default_endpoint):
kernel_user = os.environ.get('KERNEL_USERNAME')
if kernel_user:
return '?user='.join([default_endpoint, kernel_user])
return default_endpoint
def _get_kernelspecs_endpoint_url(self, kernel_name=None):
"""Builds a url for the kernels endpoint
Parameters
----------
kernel_name: kernel name (optional)
"""
if kernel_name:
return url_path_join(self.base_endpoint, url_escape(kernel_name))
return self.base_endpoint
async def get_all_specs(self):
fetched_kspecs = await self.list_kernel_specs()
# get the default kernel name and compare to that of this server.
# If different log a warning and reset the default. However, the
# caller of this method will still return this server's value until
# the next fetch of kernelspecs - at which time they'll match.
km = self.parent.kernel_manager
remote_default_kernel_name = fetched_kspecs.get('default')
if remote_default_kernel_name != km.default_kernel_name:
self.log.info("Default kernel name on Gateway server ({gateway_default}) differs from "
"Notebook server ({notebook_default}). Updating to Gateway server's value.".
format(gateway_default=remote_default_kernel_name,
notebook_default=km.default_kernel_name))
km.default_kernel_name = remote_default_kernel_name
remote_kspecs = fetched_kspecs.get('kernelspecs')
return remote_kspecs
async def list_kernel_specs(self):
"""Get a list of kernel specs."""
kernel_spec_url = self._get_kernelspecs_endpoint_url()
self.log.debug("Request list kernel specs at: %s", kernel_spec_url)
response = await gateway_request(kernel_spec_url, method='GET')
kernel_specs = json_decode(response.body)
return kernel_specs
async def get_kernel_spec(self, kernel_name, **kwargs):
"""Get kernel spec for kernel_name.
Parameters
----------
kernel_name : str
The name of the kernel.
"""
kernel_spec_url = self._get_kernelspecs_endpoint_url(kernel_name=str(kernel_name))
self.log.debug("Request kernel spec at: %s" % kernel_spec_url)
try:
response = await gateway_request(kernel_spec_url, method='GET')
except web.HTTPError as error:
if error.status_code == 404:
# Convert not found to KeyError since that's what the Notebook handler expects
# message is not used, but might as well make it useful for troubleshooting
raise KeyError(
'kernelspec {kernel_name} not found on Gateway server at: {gateway_url}'.
format(kernel_name=kernel_name, gateway_url=GatewayClient.instance().url)
) from error
else:
raise
else:
kernel_spec = json_decode(response.body)
return kernel_spec
async def get_kernel_spec_resource(self, kernel_name, path):
"""Get kernel spec for kernel_name.
Parameters
----------
kernel_name : str
The name of the kernel.
path : str
The name of the desired resource
"""
kernel_spec_resource_url = url_path_join(self.base_resource_endpoint, str(kernel_name), str(path))
self.log.debug("Request kernel spec resource '{}' at: {}".format(path, kernel_spec_resource_url))
try:
response = await gateway_request(kernel_spec_resource_url, method='GET')
except web.HTTPError as error:
if error.status_code == 404:
kernel_spec_resource = None
else:
raise
else:
kernel_spec_resource = response.body
return kernel_spec_resource
class GatewaySessionManager(SessionManager):
kernel_manager = Instance('notebook.gateway.managers.GatewayKernelManager')
async def kernel_culled(self, kernel_id):
"""Checks if the kernel is still considered alive and returns true if its not found. """
kernel = await self.kernel_manager.get_kernel(kernel_id)
return kernel is None

@ -1,134 +0,0 @@
# Implementation Notes for Internationalization of Jupyter Notebook
The implementation of i18n features for jupyter notebook is still a work-in-progress:
- User interface strings are (mostly) handled
- Console messages are not handled (their usefulness in a translated environment is questionable)
- Tooling has to be refined
However…
## How the language is selected ?
1. `jupyter notebook` command reads the `LANG` environment variable at startup,
(`xx_XX` or just `xx` form, where `xx` is the language code you're wanting to
run in).
Hint: if running Windows, you can set it in PowerShell with `${Env:LANG} = "xx_XX"`.
if running Ubuntu 14, you should set environment variable `LANGUAGE="xx_XX"`.
2. The preferred language for web pages in your browser settings (`xx`) is
also used. At the moment, it has to be first in the list.
## Contributing and managing translations
Finding and translating the `.pot` files could be (partially) done with a translation API, see the repo [Jupyter Notebook Azure Translator](https://github.com/berendjan/Jupyter-Notebook-Azure-Translator.git) for a possible starting point. (Not affiliated with Jupyter)
### Requirements
- *pybabel* (could be installed `pip install babel`)
- *po2json* (could be installed with `npm install -g po2json`)
**All i18n-related commands are done from the related directory :**
cd notebook/i18n/
### Message extraction
The translatable material for notebook is split into 3 `.pot` files, as follows:
- *notebook/i18n/notebook.pot* - Console and startup messages, basically anything that is
produced by Python code.
- *notebook/i18n/nbui.pot* - User interface strings, as extracted from the Jinja2 templates
in *notebook/templates/\*.html*
- *noteook/i18n/nbjs.pot* - JavaScript strings and dialogs, which contain much of the visible
user interface for Jupyter notebook.
To extract the messages from the source code whenever new material is added, use the
`pybabel` command:
```shell
pybabel extract -F babel_notebook.cfg -o notebook.pot --no-wrap --project Jupyter .
pybabel extract -F babel_nbui.cfg -o nbui.pot --no-wrap --project Jupyter .
pybabel extract -F babel_nbjs.cfg -o nbjs.pot --no-wrap --project Jupyter .
```
After this is complete you have 3 `.pot` files that you can give to a translator for your favorite language.
Finding and translating the `.pot` files could be (partially done with a translation API, see the repo [Jupyter Notebook Azure Translator](https://github.com/berendjan/Jupyter-Notebook-Azure-Translator.git) for a possible starting point. (Not affiliated with Jupyter)
### Messages compilation
After the source material has been translated, you should have 3 `.po` files with the same base names
as the `.pot` files above. Put them in `notebook/i18n/${LANG}/LC_MESSAGES`, where `${LANG}` is the language
code for your desired language ( i.e. German = "de", Japanese = "ja", etc. ).
*notebook.po* and *nbui.po* need to be converted from `.po` to `.mo` format for
use at runtime.
```shell
pybabel compile -D notebook -f -l ${LANG} -i ${LANG}/LC_MESSAGES/notebook.po -o ${LANG}/LC_MESSAGES/notebook.mo
pybabel compile -D nbui -f -l ${LANG} -i ${LANG}/LC_MESSAGES/nbui.po -o ${LANG}/LC_MESSAGES/nbui.mo
```
*nbjs.po* needs to be converted to JSON for use within the JavaScript code, with *po2json*, as follows:
po2json -p -F -f jed1.x -d nbjs ${LANG}/LC_MESSAGES/nbjs.po ${LANG}/LC_MESSAGES/nbjs.json
When new languages get added, their language codes should be added to *notebook/i18n/nbjs.json*
under the `supported_languages` element.
### Tips for Jupyter developers
The biggest "mistake" I found while doing i18n enablement was the habit of constructing UI messages
from English "piece parts". For example, code like:
```javascript
var msg = "Enter a new " + type + "name:"
```
where `type` is either "file", "directory", or "notebook"....
is problematic when doing translations, because the surrounding text may need to vary
depending on the inserted word. In this case, you need to switch it and use complete phrases,
as follows:
```javascript
var rename_msg = function (type) {
switch(type) {
case 'file': return _("Enter a new file name:");
case 'directory': return _("Enter a new directory name:");
case 'notebook': return _("Enter a new notebook name:");
default: return _("Enter a new name:");
}
}
```
Also you need to remember that adding an "s" or "es" to an English word to
create the plural form doesn't translate well. Some languages have as many as 5 or 6 different
plural forms for differing numbers, so using an API such as ngettext() is necessary in order
to handle these cases properly.
### Known issues and future evolutions
1. Right now there are two different places where the desired language is set. At startup time, the Jupyter console's messages pay attention to the setting of the `${LANG}` environment variable
as set in the shell at startup time. Unfortunately, this is also the time where the Jinja2
environment is set up, which means that the template stuff will always come from this setting.
We really want to be paying attention to the browser's settings for the stuff that happens in the
browser, so we need to be able to retrieve this information after the browser is started and somehow
communicate this back to Jinja2. So far, I haven't yet figured out how to do this, which means that if the ${LANG} at startup doesn't match the browser's settings, you could potentially get a mix
of languages in the UI ( never a good thing ).
2. We will need to decide if console messages should be translatable, and enable them if desired.
3. The keyboard shortcut editor was implemented after the i18n work was completed, so that portion
does not have translation support at this time.
4. Babel's documentation has instructions on how to integrate messages extraction
into your *setup.py* so that eventually we can just do:
./setup.py extract_messages
I hope to get this working at some point in the near future.
5. The conversions from `.po` to `.mo` probably can and should be done using `setup.py install`.
Any questions or comments please let me know @JCEmmons on github (emmo@us.ibm.com)

@ -1,103 +0,0 @@
"""Server functions for loading translations
"""
from collections import defaultdict
import errno
import io
import json
from os.path import dirname, join as pjoin
import re
I18N_DIR = dirname(__file__)
# Cache structure:
# {'nbjs': { # Domain
# 'zh-CN': { # Language code
# <english string>: <translated string>
# ...
# }
# }}
TRANSLATIONS_CACHE = {'nbjs': {}}
_accept_lang_re = re.compile(r'''
(?P<lang>[a-zA-Z]{1,8}(-[a-zA-Z]{1,8})?)
(\s*;\s*q\s*=\s*
(?P<qvalue>[01](.\d+)?)
)?''', re.VERBOSE)
def parse_accept_lang_header(accept_lang):
"""Parses the 'Accept-Language' HTTP header.
Returns a list of language codes in *ascending* order of preference
(with the most preferred language last).
"""
by_q = defaultdict(list)
for part in accept_lang.split(','):
m = _accept_lang_re.match(part.strip())
if not m:
continue
lang, qvalue = m.group('lang', 'qvalue')
# Browser header format is zh-CN, gettext uses zh_CN
lang = lang.replace('-', '_')
if qvalue is None:
qvalue = 1.
else:
qvalue = float(qvalue)
if qvalue == 0:
continue # 0 means not accepted
by_q[qvalue].append(lang)
if '_' in lang:
short = lang.split('_')[0]
if short != 'en':
by_q[qvalue].append(short)
res = []
for qvalue, langs in sorted(by_q.items()):
res.extend(sorted(langs))
return res
def load(language, domain='nbjs'):
"""Load translations from an nbjs.json file"""
try:
f = io.open(pjoin(I18N_DIR, language, 'LC_MESSAGES', 'nbjs.json'),
encoding='utf-8')
except IOError as e:
if e.errno != errno.ENOENT:
raise
return {}
with f:
data = json.load(f)
return data["locale_data"][domain]
def cached_load(language, domain='nbjs'):
"""Load translations for one language, using in-memory cache if available"""
domain_cache = TRANSLATIONS_CACHE[domain]
try:
return domain_cache[language]
except KeyError:
data = load(language, domain)
domain_cache[language] = data
return data
def combine_translations(accept_language, domain='nbjs'):
"""Combine translations for multiple accepted languages.
Returns data re-packaged in jed1.x format.
"""
lang_codes = parse_accept_lang_header(accept_language)
combined = {}
for language in lang_codes:
if language == 'en':
# en is default, all translations are in frontend.
combined.clear()
else:
combined.update(cached_load(language, domain))
combined[''] = {"domain":"nbjs"}
return {
"domain": domain,
"locale_data": {
domain: combined
}
}

@ -1,11 +0,0 @@
[javascript: notebook/static/base/js/*.js]
extract_messages = $._, i18n.msg._
[javascript: notebook/static/notebook/js/*.js]
extract_messages = $._, i18n.msg._
[javascript: notebook/static/notebook/js/celltoolbarpresets/*.js]
extract_messages = $._, i18n.msg._
[javascript: notebook/static/tree/js/*.js]
extract_messages = $._, i18n.msg._

@ -1,4 +0,0 @@
[jinja2: notebook/templates/**.html]
encoding = utf-8
[extractors]
jinja2 = jinja2.ext:babel_extract

@ -1,2 +0,0 @@
[python: notebook/*.py]
[python: notebook/services/contents/*.py]

File diff suppressed because it is too large Load Diff

@ -1,745 +0,0 @@
# Translations template for Jupyter.
# Copyright (C) 2017 ORGANIZATION
# This file is distributed under the same license as the Jupyter project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
#
msgid ""
msgstr ""
"Project-Id-Version: Jupyter VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2018-08-29 17:49+0200\n"
"PO-Revision-Date: 2018-09-15 17:55+0200\n"
"Last-Translator: Jocelyn Delalande <jocelyn@delalande.fr>\n"
"Language-Team: \n"
"Language: fr_FR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
"X-Generator: Poedit 1.8.11\n"
#: notebook/templates/404.html:3
msgid "You are requesting a page that does not exist!"
msgstr "Vous demandez une page qui n'existe pas !"
#: notebook/templates/edit.html:37
msgid "current mode"
msgstr "mode actuel"
#: notebook/templates/edit.html:48 notebook/templates/notebook.html:78
msgid "File"
msgstr "Fichier"
#: notebook/templates/edit.html:50 notebook/templates/tree.html:57
msgid "New"
msgstr "Nouveau"
#: notebook/templates/edit.html:51
msgid "Save"
msgstr "Enregistrer"
#: notebook/templates/edit.html:52 notebook/templates/tree.html:36
msgid "Rename"
msgstr "Renommer"
#: notebook/templates/edit.html:53 notebook/templates/tree.html:38
msgid "Download"
msgstr "Télécharger"
#: notebook/templates/edit.html:56 notebook/templates/notebook.html:131
#: notebook/templates/tree.html:41
msgid "Edit"
msgstr "Édition"
#: notebook/templates/edit.html:58
msgid "Find"
msgstr "Rechercher"
#: notebook/templates/edit.html:59
msgid "Find &amp; Replace"
msgstr "Rechercher &amp; Remplacer"
#: notebook/templates/edit.html:61
msgid "Key Map"
msgstr "Raccourcis clavier"
#: notebook/templates/edit.html:62
msgid "Default"
msgstr "Par Défaut"
#: notebook/templates/edit.html:63
msgid "Sublime Text"
msgstr "Sublime Text"
#: notebook/templates/edit.html:68 notebook/templates/notebook.html:159
#: notebook/templates/tree.html:40
msgid "View"
msgstr "Affichage"
#: notebook/templates/edit.html:70 notebook/templates/notebook.html:162
msgid "Show/Hide the logo and notebook title (above menu bar)"
msgstr "Afficher/Masquer le logo et le titre du notebook (au-dessus de la "
"barre de menu)"
#: notebook/templates/edit.html:71 notebook/templates/notebook.html:163
msgid "Toggle Header"
msgstr "Afficher/Masquer l'en-tête"
#: notebook/templates/edit.html:72 notebook/templates/notebook.html:171
msgid "Toggle Line Numbers"
msgstr "Afficher/Masquer les numéros de ligne"
#: notebook/templates/edit.html:75
msgid "Language"
msgstr "Langage"
#: notebook/templates/error.html:23
msgid "The error was:"
msgstr "L'erreur était :"
#: notebook/templates/login.html:24
msgid "Password or token:"
msgstr "Mot de passe ou jeton:"
#: notebook/templates/login.html:26
msgid "Password:"
msgstr "Mot de passe :"
#: notebook/templates/login.html:31
msgid "Log in"
msgstr "Se connecter"
#: notebook/templates/login.html:39
msgid "No login available, you shouldn't be seeing this page."
msgstr "Connexion non disponible, vous ne devriez pas voir cette page."
#: notebook/templates/logout.html:24
#, python-format
msgid "Proceed to the <a href=\"%(base_url)s\">dashboard"
msgstr "Continuer vers le <a href=\"%(base_url)s\">tableau de bord"
#: notebook/templates/logout.html:26
#, python-format
msgid "Proceed to the <a href=\"%(base_url)slogin\">login page"
msgstr "Continuer vers la <a href=\"%(base_url)slogin\">page de connexion"
#: notebook/templates/notebook.html:62
msgid "Menu"
msgstr "Menu"
#: notebook/templates/notebook.html:65 notebook/templates/notebook.html:254
msgid "Kernel"
msgstr "Noyau"
#: notebook/templates/notebook.html:68
msgid "This notebook is read-only"
msgstr "Ce notebook est en lecture seule"
#: notebook/templates/notebook.html:81
msgid "New Notebook"
msgstr "Nouveau Notebook"
#: notebook/templates/notebook.html:85
msgid "Opens a new window with the Dashboard view"
msgstr "Ouvre une nouvelle fenêtre de tableau de bord"
#: notebook/templates/notebook.html:86
msgid "Open..."
msgstr "Ouvrir..."
#: notebook/templates/notebook.html:90
msgid "Open a copy of this notebook's contents and start a new kernel"
msgstr "Ouvrir une copie du contenu de ce notebook et démarrer un nouveau noyau"
#: notebook/templates/notebook.html:91
msgid "Make a Copy..."
msgstr "Faire une copie..."
#: notebook/templates/notebook.html:92
msgid "Rename..."
msgstr "Renommer..."
#: notebook/templates/notebook.html:93
msgid "Save and Checkpoint"
msgstr "Créer une nouvelle sauvegarde"
#: notebook/templates/notebook.html:96
msgid "Revert to Checkpoint"
msgstr "Restaurer la sauvegarde"
#: notebook/templates/notebook.html:106
msgid "Print Preview"
msgstr "Imprimer l'aperçu"
#: notebook/templates/notebook.html:107
msgid "Download as"
msgstr "Télécharger au format"
#: notebook/templates/notebook.html:109
msgid "Notebook (.ipynb)"
msgstr "Notebook (.ipynb)"
#: notebook/templates/notebook.html:110
msgid "Script"
msgstr "Script"
#: notebook/templates/notebook.html:111
msgid "HTML (.html)"
msgstr "HTML (.html)"
#: notebook/templates/notebook.html:112
msgid "Markdown (.md)"
msgstr "Markdown (.md)"
#: notebook/templates/notebook.html:113
msgid "reST (.rst)"
msgstr "reST (.rst)"
#: notebook/templates/notebook.html:114
msgid "LaTeX (.tex)"
msgstr "LaTeX (.tex)"
#: notebook/templates/notebook.html:115
msgid "PDF via LaTeX (.pdf)"
msgstr "PDF via LaTeX (.pdf)"
#: notebook/templates/notebook.html:118
msgid "Deploy as"
msgstr "Déployer en tant que"
#: notebook/templates/notebook.html:123
msgid "Trust the output of this notebook"
msgstr "Faire confiance à la sortie de ce notebook"
#: notebook/templates/notebook.html:124
msgid "Trust Notebook"
msgstr "Faire confiance au notebook"
#: notebook/templates/notebook.html:127
msgid "Shutdown this notebook's kernel, and close this window"
msgstr "Arrêter le noyau de ce notebook et fermer cette fenêtre"
#: notebook/templates/notebook.html:128
msgid "Close and Halt"
msgstr "Fermer et arrêter"
#: notebook/templates/notebook.html:133
msgid "Cut Cells"
msgstr "Couper les cellules"
#: notebook/templates/notebook.html:134
msgid "Copy Cells"
msgstr "Copier les cellules"
#: notebook/templates/notebook.html:135
msgid "Paste Cells Above"
msgstr "Coller les cellules avant"
#: notebook/templates/notebook.html:136
msgid "Paste Cells Below"
msgstr "Coller les cellules après"
#: notebook/templates/notebook.html:137
msgid "Paste Cells &amp; Replace"
msgstr "Coller les cellules &amp; remplacer"
#: notebook/templates/notebook.html:138
msgid "Delete Cells"
msgstr "Supprimer les cellules"
#: notebook/templates/notebook.html:139
msgid "Undo Delete Cells"
msgstr "Annuler la suppression des cellules"
#: notebook/templates/notebook.html:141
msgid "Split Cell"
msgstr "Diviser la cellule"
#: notebook/templates/notebook.html:142
msgid "Merge Cell Above"
msgstr "Fusionner avec la cellule précédente"
#: notebook/templates/notebook.html:143
msgid "Merge Cell Below"
msgstr "Fusionner avec la cellule suivante"
#: notebook/templates/notebook.html:145
msgid "Move Cell Up"
msgstr "Déplacer la cellule vers le haut"
#: notebook/templates/notebook.html:146
msgid "Move Cell Down"
msgstr "Déplacer la cellule vers le bas"
#: notebook/templates/notebook.html:148
msgid "Edit Notebook Metadata"
msgstr "Éditer les méta-données du notebook"
#: notebook/templates/notebook.html:150
msgid "Find and Replace"
msgstr "Rechercher et remplacer"
#: notebook/templates/notebook.html:152
msgid "Cut Cell Attachments"
msgstr "Couper les pièces-Jointes de la cellule"
#: notebook/templates/notebook.html:153
msgid "Copy Cell Attachments"
msgstr "Copier les pièces-jointes de la cellule"
#: notebook/templates/notebook.html:154
msgid "Paste Cell Attachments"
msgstr "Coller les pièces-jointes de la cellule"
#: notebook/templates/notebook.html:156
msgid "Insert Image"
msgstr "Insérer une image"
#: notebook/templates/notebook.html:166
msgid "Show/Hide the action icons (below menu bar)"
msgstr "Afficher/Masquer les icônes d'action (en-dessous de la barre de menu)"
#: notebook/templates/notebook.html:167
msgid "Toggle Toolbar"
msgstr "Afficher/Masquer la barre d'outils"
#: notebook/templates/notebook.html:170
msgid "Show/Hide line numbers in cells"
msgstr "Afficher/Masquer les numéros de ligne dans les cellules"
#: notebook/templates/notebook.html:174
msgid "Cell Toolbar"
msgstr "Barre d'outil de cellule"
#: notebook/templates/notebook.html:179
msgid "Insert"
msgstr "Insérer"
#: notebook/templates/notebook.html:182
msgid "Insert an empty Code cell above the currently active cell"
msgstr "Insérer une cellule de code vide avant de la cellule active"
#: notebook/templates/notebook.html:183
msgid "Insert Cell Above"
msgstr "Insérer une cellule avant"
#: notebook/templates/notebook.html:185
msgid "Insert an empty Code cell below the currently active cell"
msgstr "Insérer une cellule de code vide après la cellule active"
#: notebook/templates/notebook.html:186
msgid "Insert Cell Below"
msgstr "Insérer une cellule après"
#: notebook/templates/notebook.html:189
msgid "Cell"
msgstr "Cellule"
#: notebook/templates/notebook.html:191
msgid "Run this cell, and move cursor to the next one"
msgstr "Exécuter cette cellule, et déplacer le curseur à la suivante"
#: notebook/templates/notebook.html:192
msgid "Run Cells"
msgstr "Exécuter les cellules"
#: notebook/templates/notebook.html:193
msgid "Run this cell, select below"
msgstr "Exécuter cette cellule, sélectionner la suivante"
#: notebook/templates/notebook.html:194
msgid "Run Cells and Select Below"
msgstr "Exécuter les cellules et sélectionner la suivante"
#: notebook/templates/notebook.html:195
msgid "Run this cell, insert below"
msgstr "Exécuter la cellule et insérer à la suite"
#: notebook/templates/notebook.html:196
msgid "Run Cells and Insert Below"
msgstr "Exécuter les cellules et insérer après"
#: notebook/templates/notebook.html:197
msgid "Run all cells in the notebook"
msgstr "Exécuter toutes les cellules du notebook"
#: notebook/templates/notebook.html:198
msgid "Run All"
msgstr "Exécuter tout"
#: notebook/templates/notebook.html:199
msgid "Run all cells above (but not including) this cell"
msgstr "Exécuter toutes les cellules avant celle-ci (non incluse)"
#: notebook/templates/notebook.html:200
msgid "Run All Above"
msgstr "Exécuter toutes les précédentes"
#: notebook/templates/notebook.html:201
msgid "Run this cell and all cells below it"
msgstr "Exécuter cette cellule et toutes les suivantes"
#: notebook/templates/notebook.html:202
msgid "Run All Below"
msgstr "Exécuter toutes les suivantes"
#: notebook/templates/notebook.html:205
msgid ""
"All cells in the notebook have a cell type. By default, new cells are "
"created as 'Code' cells"
msgstr ""
"Toutes les cellules dans le notebook ont un type de "
"cellule. Par défaut, les nouvelles cellules sont de type 'Code'"
#: notebook/templates/notebook.html:206
msgid "Cell Type"
msgstr "Type de cellule"
#: notebook/templates/notebook.html:209
msgid ""
"Contents will be sent to the kernel for execution, and output will display "
"in the footer of cell"
msgstr ""
"Le contenu sera envoyé au noyau pour exécution, et la sortie sera affichée "
"dans le pied de cellule"
#: notebook/templates/notebook.html:212
msgid "Contents will be rendered as HTML and serve as explanatory text"
msgstr ""
"Le contenu sera rendu en tant que HTML afin de servir de texte explicatif"
#: notebook/templates/notebook.html:213 notebook/templates/notebook.html:298
msgid "Markdown"
msgstr "Markdown"
#: notebook/templates/notebook.html:215
msgid "Contents will pass through nbconvert unmodified"
msgstr "Le contenu passera par nbconvert qui ne l'altèrera pas"
#: notebook/templates/notebook.html:216
msgid "Raw NBConvert"
msgstr "Texte Brut (pour NBConvert)"
#: notebook/templates/notebook.html:220
msgid "Current Outputs"
msgstr "Sorties actuelles"
#: notebook/templates/notebook.html:223
msgid "Hide/Show the output of the current cell"
msgstr "Masquer/Afficher la sortie de la cellule actuelle"
#: notebook/templates/notebook.html:224 notebook/templates/notebook.html:240
msgid "Toggle"
msgstr "Afficher/Masquer"
#: notebook/templates/notebook.html:227
msgid "Scroll the output of the current cell"
msgstr "Faire défiler la sortie de la cellule actuelle"
#: notebook/templates/notebook.html:228 notebook/templates/notebook.html:244
msgid "Toggle Scrolling"
msgstr "Activer/Désactiver le défilement"
#: notebook/templates/notebook.html:231
msgid "Clear the output of the current cell"
msgstr "Effacer la sortie de la cellule actuelle"
#: notebook/templates/notebook.html:232 notebook/templates/notebook.html:248
msgid "Clear"
msgstr "Effacer"
#: notebook/templates/notebook.html:236
msgid "All Output"
msgstr "Toute la sortie"
#: notebook/templates/notebook.html:239
msgid "Hide/Show the output of all cells"
msgstr "Afficher/Masquer la sortie de toutes les cellules"
#: notebook/templates/notebook.html:243
msgid "Scroll the output of all cells"
msgstr "Faire défiler la sortie de toutes les cellules"
#: notebook/templates/notebook.html:247
msgid "Clear the output of all cells"
msgstr "Effacer la sortie de toutes les cellules"
#: notebook/templates/notebook.html:257
msgid "Send Keyboard Interrupt (CTRL-C) to the Kernel"
msgstr "Envoyer l'interruption clavier (CTRL-C) au noyau"
#: notebook/templates/notebook.html:258
msgid "Interrupt"
msgstr "Interrompre"
#: notebook/templates/notebook.html:261
msgid "Restart the Kernel"
msgstr "Redémarrer le noyau"
#: notebook/templates/notebook.html:262
msgid "Restart"
msgstr "Redémarrer"
#: notebook/templates/notebook.html:265
msgid "Restart the Kernel and clear all output"
msgstr "Redémarrer le noyau et effacer toutes les sorties"
#: notebook/templates/notebook.html:266
msgid "Restart &amp; Clear Output"
msgstr "Redémarrer &amp; effacer les sorties"
#: notebook/templates/notebook.html:269
msgid "Restart the Kernel and re-run the notebook"
msgstr "Redémarrer le noyau et ré-exécuter le noteboook"
#: notebook/templates/notebook.html:270
msgid "Restart &amp; Run All"
msgstr "Redémarrer &amp; tout exécuter"
#: notebook/templates/notebook.html:273
msgid "Reconnect to the Kernel"
msgstr "Reconnecter au noyau"
#: notebook/templates/notebook.html:274
msgid "Reconnect"
msgstr "Reconnecter"
#: notebook/templates/notebook.html:282
msgid "Change kernel"
msgstr "Changer de noyau"
#: notebook/templates/notebook.html:287
msgid "Help"
msgstr "Aide"
#: notebook/templates/notebook.html:290
msgid "A quick tour of the notebook user interface"
msgstr "Une rapide visite de l'interface utilisateur du notebook"
#: notebook/templates/notebook.html:290
msgid "User Interface Tour"
msgstr "Visite de l'interface utilisateur"
#: notebook/templates/notebook.html:291
msgid "Opens a tooltip with all keyboard shortcuts"
msgstr "Ouvre une infobulle listant tous les raccourcis clavier"
#: notebook/templates/notebook.html:291
msgid "Keyboard Shortcuts"
msgstr "Raccourcis clavier"
#: notebook/templates/notebook.html:292
msgid "Opens a dialog allowing you to edit Keyboard shortcuts"
msgstr "Ouvre une boîte de dialogue permettant de modifier les raccourcis clavier"
#: notebook/templates/notebook.html:292
msgid "Edit Keyboard Shortcuts"
msgstr "Editer les raccourcis clavier"
#: notebook/templates/notebook.html:297
msgid "Notebook Help"
msgstr "Aide notebook"
#: notebook/templates/notebook.html:303
msgid "Opens in a new window"
msgstr "S'ouvre dans une nouvelle fenêtre"
#: notebook/templates/notebook.html:319
msgid "About Jupyter Notebook"
msgstr "À propos de Jupyter Notebook"
#: notebook/templates/notebook.html:319
msgid "About"
msgstr "À propos"
#: notebook/templates/page.html:114
msgid "Jupyter Notebook requires JavaScript."
msgstr "Jupyter Notebook nécessite JavaScript"
#: notebook/templates/page.html:115
msgid "Please enable it to proceed. "
msgstr "Merci de l'activer pour continuer."
#: notebook/templates/page.html:121
msgid "dashboard"
msgstr "tableau de bord"
#: notebook/templates/page.html:132
msgid "Logout"
msgstr "Se déconnecter"
#: notebook/templates/page.html:134
msgid "Login"
msgstr "Se connecter"
#: notebook/templates/tree.html:23
msgid "Files"
msgstr "Fichiers"
#: notebook/templates/tree.html:24
msgid "Running"
msgstr "Actifs"
#: notebook/templates/tree.html:25
msgid "Clusters"
msgstr "Grappes"
#: notebook/templates/tree.html:32
msgid "Select items to perform actions on them."
msgstr "Sélectionner des éléments pour leur appliquer des actions."
#: notebook/templates/tree.html:35
msgid "Duplicate selected"
msgstr "Dupliquer la sélection"
#: notebook/templates/tree.html:35
msgid "Duplicate"
msgstr "Dupliquer"
#: notebook/templates/tree.html:36
msgid "Rename selected"
msgstr "Renommer la sélection"
#: notebook/templates/tree.html:37
msgid "Move selected"
msgstr "Déplacer la sélection"
#: notebook/templates/tree.html:37
msgid "Move"
msgstr "Déplacer"
#: notebook/templates/tree.html:38
msgid "Download selected"
msgstr "Télécharger la sélection"
#: notebook/templates/tree.html:39
msgid "Shutdown selected notebook(s)"
msgstr "Arrêter le(s) notebook(s) sélectionné(s)"
#: notebook/templates/notebook.html:278 notebook/templates/tree.html:39
msgid "Shutdown"
msgstr "Arrêter"
#: notebook/templates/tree.html:40
msgid "View selected"
msgstr "Voir la sélection"
#: notebook/templates/tree.html:41
msgid "Edit selected"
msgstr "Éditer la sélection"
#: notebook/templates/tree.html:42
msgid "Delete selected"
msgstr "Supprimer la sélection"
#: notebook/templates/tree.html:50
msgid "Click to browse for a file to upload."
msgstr "Cliquer pour choisir un fichier à téléverser"
#: notebook/templates/tree.html:51
msgid "Upload"
msgstr "Téléverser"
#: notebook/templates/tree.html:65
msgid "Text File"
msgstr "Fichier texte"
#: notebook/templates/tree.html:68
msgid "Folder"
msgstr "Répertoire"
#: notebook/templates/tree.html:72
msgid "Terminal"
msgstr "Terminal"
#: notebook/templates/tree.html:76
msgid "Terminals Unavailable"
msgstr "Terminaux non disponibles"
#: notebook/templates/tree.html:82
msgid "Refresh notebook list"
msgstr "Rafraîchir la liste des notebooks"
#: notebook/templates/tree.html:90
msgid "Select All / None"
msgstr "Sélectionner tout / aucun"
#: notebook/templates/tree.html:93
msgid "Select..."
msgstr "Sélectionner..."
#: notebook/templates/tree.html:98
msgid "Select All Folders"
msgstr "Sélectionner tous les répertoires"
#: notebook/templates/tree.html:98
msgid "Folders"
msgstr "Répertoires"
#: notebook/templates/tree.html:99
msgid "Select All Notebooks"
msgstr "Sélectionner tous les notebooks"
#: notebook/templates/tree.html:99
msgid "All Notebooks"
msgstr "Tous les notebooks"
#: notebook/templates/tree.html:100
msgid "Select Running Notebooks"
msgstr "Sélectionner les notebooks en cours d'exécution"
#: notebook/templates/tree.html:100
msgid "Running"
msgstr "Actifs"
#: notebook/templates/tree.html:101
msgid "Select All Files"
msgstr "Sélectionner tous les fichiers"
#: notebook/templates/tree.html:101
msgid "Files"
msgstr "Fichiers"
#: notebook/templates/tree.html:114
msgid "Last Modified"
msgstr "Dernière modification"
#: notebook/templates/tree.html:120
msgid "Name"
msgstr "Nom"
#: notebook/templates/tree.html:130
msgid "Currently running Jupyter processes"
msgstr "Processus Jupyter en cours d'exécution"
#: notebook/templates/tree.html:134
msgid "Refresh running list"
msgstr "Rafraîchir la liste des actifs"
#: notebook/templates/tree.html:150
msgid "There are no terminals running."
msgstr "Il n'y a aucun terminal en cours d'exécution."
#: notebook/templates/tree.html:152
msgid "Terminals are unavailable."
msgstr "Les terminaux sont indisponibles."
#: notebook/templates/tree.html:162
msgid "Notebooks"
msgstr "Notebooks"
#: notebook/templates/tree.html:169
msgid "There are no notebooks running."
msgstr "Il n'y a aucun notebook en cours d'exécution."
#: notebook/templates/tree.html:178
msgid "Clusters tab is now provided by IPython parallel."
msgstr "L'onglet des grappes est désormais fourni par IPython parallel."
#: notebook/templates/tree.html:179
msgid ""
"See '<a href=\"https://github.com/ipython/ipyparallel\">IPython parallel</"
"a>' for installation details."
msgstr ""
"Voir '<a href=\"https://github.com/ipython/ipyparallel\">IPython parallel</"
"a>' pour les détails d'installation."

@ -1,480 +0,0 @@
# Translations template for Jupyter.
# Copyright (C) 2017 ORGANIZATION
# This file is distributed under the same license as the Jupyter project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Jupyter VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2017-07-08 21:52-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
#: notebook/notebookapp.py:53
msgid "The Jupyter Notebook requires tornado >= 4.0"
msgstr ""
#: notebook/notebookapp.py:57
msgid "The Jupyter Notebook requires tornado >= 4.0, but you have < 1.1.0"
msgstr ""
#: notebook/notebookapp.py:59
#, python-format
msgid "The Jupyter Notebook requires tornado >= 4.0, but you have %s"
msgstr ""
#: notebook/notebookapp.py:209
msgid "The `ignore_minified_js` flag is deprecated and no longer works."
msgstr ""
#: notebook/notebookapp.py:210
#, python-format
msgid "Alternatively use `%s` when working on the notebook's Javascript and LESS"
msgstr ""
#: notebook/notebookapp.py:211
msgid "The `ignore_minified_js` flag is deprecated and will be removed in Notebook 6.0"
msgstr ""
#: notebook/notebookapp.py:389
msgid "List currently running notebook servers."
msgstr ""
#: notebook/notebookapp.py:393
msgid "Produce machine-readable JSON output."
msgstr ""
#: notebook/notebookapp.py:397
msgid "If True, each line of output will be a JSON object with the details from the server info file."
msgstr ""
#: notebook/notebookapp.py:402
msgid "Currently running servers:"
msgstr ""
#: notebook/notebookapp.py:419
msgid "Don't open the notebook in a browser after startup."
msgstr ""
#: notebook/notebookapp.py:423
msgid "DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib."
msgstr ""
#: notebook/notebookapp.py:439
msgid "Allow the notebook to be run from root user."
msgstr ""
#: notebook/notebookapp.py:470
msgid ""
"The Jupyter HTML Notebook.\n"
" \n"
" This launches a Tornado based HTML Notebook Server that serves up an HTML5/Javascript Notebook client."
msgstr ""
#: notebook/notebookapp.py:509
msgid "Deprecated: Use minified JS file or not, mainly use during dev to avoid JS recompilation"
msgstr ""
#: notebook/notebookapp.py:540
msgid "Set the Access-Control-Allow-Credentials: true header"
msgstr ""
#: notebook/notebookapp.py:544
msgid "Whether to allow the user to run the notebook as root."
msgstr ""
#: notebook/notebookapp.py:548
msgid "The default URL to redirect to from `/`"
msgstr ""
#: notebook/notebookapp.py:552
msgid "The IP address the notebook server will listen on."
msgstr ""
#: notebook/notebookapp.py:565
#, python-format
msgid ""
"Cannot bind to localhost, using 127.0.0.1 as default ip\n"
"%s"
msgstr ""
#: notebook/notebookapp.py:579
msgid "The port the notebook server will listen on."
msgstr ""
#: notebook/notebookapp.py:583
msgid "The number of additional ports to try if the specified port is not available."
msgstr ""
#: notebook/notebookapp.py:587
msgid "The full path to an SSL/TLS certificate file."
msgstr ""
#: notebook/notebookapp.py:591
msgid "The full path to a private key file for usage with SSL/TLS."
msgstr ""
#: notebook/notebookapp.py:595
msgid "The full path to a certificate authority certificate for SSL/TLS client authentication."
msgstr ""
#: notebook/notebookapp.py:599
msgid "The file where the cookie secret is stored."
msgstr ""
#: notebook/notebookapp.py:628
#, python-format
msgid "Writing notebook server cookie secret to %s"
msgstr ""
#: notebook/notebookapp.py:635
#, python-format
msgid "Could not set permissions on %s"
msgstr ""
#: notebook/notebookapp.py:640
msgid ""
"Token used for authenticating first-time connections to the server.\n"
"\n"
" When no password is enabled,\n"
" the default is to generate a new, random token.\n"
"\n"
" Setting to an empty string disables authentication altogether, which is NOT RECOMMENDED.\n"
" "
msgstr ""
#: notebook/notebookapp.py:650
msgid ""
"One-time token used for opening a browser.\n"
" Once used, this token cannot be used again.\n"
" "
msgstr ""
#: notebook/notebookapp.py:726
msgid ""
"Specify Where to open the notebook on startup. This is the\n"
" `new` argument passed to the standard library method `webbrowser.open`.\n"
" The behaviour is not guaranteed, but depends on browser support. Valid\n"
" values are:\n"
" 2 opens a new tab,\n"
" 1 opens a new window,\n"
" 0 opens in an existing window.\n"
" See the `webbrowser.open` documentation for details.\n"
" "
msgstr ""
#: notebook/notebookapp.py:737
msgid "DEPRECATED, use tornado_settings"
msgstr ""
#: notebook/notebookapp.py:742
msgid ""
"\n"
" webapp_settings is deprecated, use tornado_settings.\n"
msgstr ""
#: notebook/notebookapp.py:746
msgid "Supply overrides for the tornado.web.Application that the Jupyter notebook uses."
msgstr ""
#: notebook/notebookapp.py:750
msgid ""
"\n"
" Set the tornado compression options for websocket connections.\n"
"\n"
" This value will be returned from :meth:`WebSocketHandler.get_compression_options`.\n"
" None (default) will disable compression.\n"
" A dict (even an empty one) will enable compression.\n"
"\n"
" See the tornado docs for WebSocketHandler.get_compression_options for details.\n"
" "
msgstr ""
#: notebook/notebookapp.py:761
msgid "Supply overrides for terminado. Currently only supports \"shell_command\"."
msgstr ""
#: notebook/notebookapp.py:764
msgid "Extra keyword arguments to pass to `set_secure_cookie`. See tornado's set_secure_cookie docs for details."
msgstr ""
#: notebook/notebookapp.py:768
msgid ""
"Supply SSL options for the tornado HTTPServer.\n"
" See the tornado docs for details."
msgstr ""
#: notebook/notebookapp.py:772
msgid "Supply extra arguments that will be passed to Jinja environment."
msgstr ""
#: notebook/notebookapp.py:776
msgid "Extra variables to supply to jinja templates when rendering."
msgstr ""
#: notebook/notebookapp.py:812
msgid "DEPRECATED use base_url"
msgstr ""
#: notebook/notebookapp.py:816
msgid "base_project_url is deprecated, use base_url"
msgstr ""
#: notebook/notebookapp.py:832
msgid "Path to search for custom.js, css"
msgstr ""
#: notebook/notebookapp.py:844
msgid ""
"Extra paths to search for serving jinja templates.\n"
"\n"
" Can be used to override templates from notebook.templates."
msgstr ""
#: notebook/notebookapp.py:855
msgid "extra paths to look for Javascript notebook extensions"
msgstr ""
#: notebook/notebookapp.py:900
#, python-format
msgid "Using MathJax: %s"
msgstr ""
#: notebook/notebookapp.py:903
msgid "The MathJax.js configuration file that is to be used."
msgstr ""
#: notebook/notebookapp.py:908
#, python-format
msgid "Using MathJax configuration file: %s"
msgstr ""
#: notebook/notebookapp.py:914
msgid "The notebook manager class to use."
msgstr ""
#: notebook/notebookapp.py:920
msgid "The kernel manager class to use."
msgstr ""
#: notebook/notebookapp.py:926
msgid "The session manager class to use."
msgstr ""
#: notebook/notebookapp.py:932
msgid "The config manager class to use"
msgstr ""
#: notebook/notebookapp.py:953
msgid "The login handler class to use."
msgstr ""
#: notebook/notebookapp.py:960
msgid "The logout handler class to use."
msgstr ""
#: notebook/notebookapp.py:964
msgid "Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headerssent by the upstream reverse proxy. Necessary if the proxy handles SSL"
msgstr ""
#: notebook/notebookapp.py:976
msgid ""
"\n"
" DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib.\n"
" "
msgstr ""
#: notebook/notebookapp.py:988
msgid "Support for specifying --pylab on the command line has been removed."
msgstr ""
#: notebook/notebookapp.py:990
msgid "Please use `%pylab{0}` or `%matplotlib{0}` in the notebook itself."
msgstr ""
#: notebook/notebookapp.py:995
msgid "The directory to use for notebooks and kernels."
msgstr ""
#: notebook/notebookapp.py:1018
#, python-format
msgid "No such notebook dir: '%r'"
msgstr ""
#: notebook/notebookapp.py:1031
msgid "DEPRECATED use the nbserver_extensions dict instead"
msgstr ""
#: notebook/notebookapp.py:1036
msgid "server_extensions is deprecated, use nbserver_extensions"
msgstr ""
#: notebook/notebookapp.py:1040
msgid "Dict of Python modules to load as notebook server extensions.Entry values can be used to enable and disable the loading ofthe extensions. The extensions will be loaded in alphabetical order."
msgstr ""
#: notebook/notebookapp.py:1049
msgid "Reraise exceptions encountered loading server extensions?"
msgstr ""
#: notebook/notebookapp.py:1052
msgid ""
"(msgs/sec)\n"
" Maximum rate at which messages can be sent on iopub before they are\n"
" limited."
msgstr ""
#: notebook/notebookapp.py:1056
msgid ""
"(bytes/sec)\n"
" Maximum rate at which stream output can be sent on iopub before they are\n"
" limited."
msgstr ""
#: notebook/notebookapp.py:1060
msgid ""
"(sec) Time window used to \n"
" check the message and data rate limits."
msgstr ""
#: notebook/notebookapp.py:1071
#, python-format
msgid "No such file or directory: %s"
msgstr ""
#: notebook/notebookapp.py:1141
msgid "Notebook servers are configured to only be run with a password."
msgstr ""
#: notebook/notebookapp.py:1142
msgid "Hint: run the following command to set a password"
msgstr ""
#: notebook/notebookapp.py:1143
msgid "\t$ python -m notebook.auth password"
msgstr ""
#: notebook/notebookapp.py:1181
#, python-format
msgid "The port %i is already in use, trying another port."
msgstr ""
#: notebook/notebookapp.py:1184
#, python-format
msgid "Permission to listen on port %i denied"
msgstr ""
#: notebook/notebookapp.py:1193
msgid "ERROR: the notebook server could not be started because no available port could be found."
msgstr ""
#: notebook/notebookapp.py:1199
msgid "[all ip addresses on your system]"
msgstr ""
#: notebook/notebookapp.py:1223
#, python-format
msgid "Terminals not available (error was %s)"
msgstr ""
#: notebook/notebookapp.py:1259
msgid "interrupted"
msgstr ""
#: notebook/notebookapp.py:1261
msgid "y"
msgstr ""
#: notebook/notebookapp.py:1262
msgid "n"
msgstr ""
#: notebook/notebookapp.py:1263
#, python-format
msgid "Shutdown this notebook server (%s/[%s])? "
msgstr ""
#: notebook/notebookapp.py:1269
msgid "Shutdown confirmed"
msgstr ""
#: notebook/notebookapp.py:1273
msgid "No answer for 5s:"
msgstr ""
#: notebook/notebookapp.py:1274
msgid "resuming operation..."
msgstr ""
#: notebook/notebookapp.py:1282
#, python-format
msgid "received signal %s, stopping"
msgstr ""
#: notebook/notebookapp.py:1338
#, python-format
msgid "Error loading server extension %s"
msgstr ""
#: notebook/notebookapp.py:1369
#, python-format
msgid "Shutting down %d kernels"
msgstr ""
#: notebook/notebookapp.py:1375
#, python-format
msgid "%d active kernel"
msgid_plural "%d active kernels"
msgstr[0] ""
msgstr[1] ""
#: notebook/notebookapp.py:1379
#, python-format
msgid ""
"The Jupyter Notebook is running at:\n"
"\r"
"%s"
msgstr ""
#: notebook/notebookapp.py:1426
msgid "Running as root is not recommended. Use --allow-root to bypass."
msgstr ""
#: notebook/notebookapp.py:1432
msgid "Use Control-C to stop this server and shut down all kernels (twice to skip confirmation)."
msgstr ""
#: notebook/notebookapp.py:1434
msgid "Welcome to Project Jupyter! Explore the various tools available and their corresponding documentation. If you are interested in contributing to the platform, please visit the communityresources section at http://jupyter.org/community.html."
msgstr ""
#: notebook/notebookapp.py:1445
#, python-format
msgid "No web browser found: %s."
msgstr ""
#: notebook/notebookapp.py:1450
#, python-format
msgid "%s does not exist"
msgstr ""
#: notebook/notebookapp.py:1484
msgid "Interrupted..."
msgstr ""
#: notebook/services/contents/filemanager.py:506
#, python-format
msgid "Serving notebooks from local directory: %s"
msgstr ""
#: notebook/services/contents/manager.py:68
msgid "Untitled"
msgstr ""

File diff suppressed because it is too large Load Diff

@ -1,740 +0,0 @@
# Translations template for Jupyter.
# Copyright (C) 2017 ORGANIZATION
# This file is distributed under the same license as the Jupyter project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Jupyter VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2017-07-07 12:48-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
#: notebook/templates/404.html:3
msgid "You are requesting a page that does not exist!"
msgstr "要求したページは存在しません!"
#: notebook/templates/edit.html:37
msgid "current mode"
msgstr "現在のモード"
#: notebook/templates/edit.html:48 notebook/templates/notebook.html:78
msgid "File"
msgstr "ファイル"
#: notebook/templates/edit.html:50 notebook/templates/tree.html:57
msgid "New"
msgstr "新規"
#: notebook/templates/edit.html:51
msgid "Save"
msgstr "保存"
#: notebook/templates/edit.html:52 notebook/templates/tree.html:36
msgid "Rename"
msgstr "リネーム"
#: notebook/templates/edit.html:53 notebook/templates/tree.html:38
msgid "Download"
msgstr "ダウンロード"
#: notebook/templates/edit.html:56 notebook/templates/notebook.html:131
#: notebook/templates/tree.html:41
msgid "Edit"
msgstr "編集"
#: notebook/templates/edit.html:58
msgid "Find"
msgstr "検索"
#: notebook/templates/edit.html:59
msgid "Find &amp; Replace"
msgstr "検索と置換"
#: notebook/templates/edit.html:61
msgid "Key Map"
msgstr "キーマッピング"
#: notebook/templates/edit.html:62
msgid "Default"
msgstr "デフォルト"
#: notebook/templates/edit.html:63
msgid "Sublime Text"
msgstr ""
#: notebook/templates/edit.html:68 notebook/templates/notebook.html:159
#: notebook/templates/tree.html:40
msgid "View"
msgstr "表示"
#: notebook/templates/edit.html:70 notebook/templates/notebook.html:162
msgid "Show/Hide the logo and notebook title (above menu bar)"
msgstr "ロゴとノートブックのタイトルを表示/非表示 (メニューバーの上)"
#: notebook/templates/edit.html:71 notebook/templates/notebook.html:163
msgid "Toggle Header"
msgstr "ヘッダをトグル"
#: notebook/templates/edit.html:72 notebook/templates/notebook.html:171
msgid "Toggle Line Numbers"
msgstr "行番号をトグル"
#: notebook/templates/edit.html:75
msgid "Language"
msgstr "言語"
#: notebook/templates/error.html:23
msgid "The error was:"
msgstr "エラー内容:"
#: notebook/templates/login.html:24
msgid "Password or token:"
msgstr "パスワードまたはトークン:"
#: notebook/templates/login.html:26
msgid "Password:"
msgstr "パスワード:"
#: notebook/templates/login.html:31
msgid "Log in"
msgstr "ログイン"
#: notebook/templates/login.html:39
msgid "No login available, you shouldn't be seeing this page."
msgstr "ログインしていないのでこのページを見る事はできません。"
#: notebook/templates/logout.html:24
#, python-format
msgid "Proceed to the <a href=\"%(base_url)s\">dashboard"
msgstr "<a href=\"%(base_url)s\">ダッシュボード"
#: notebook/templates/logout.html:26
#, python-format
msgid "Proceed to the <a href=\"%(base_url)slogin\">login page"
msgstr "<a href=\"%(base_url)slogin\">ログインページ"
#: notebook/templates/notebook.html:62
msgid "Menu"
msgstr "メニュー"
#: notebook/templates/notebook.html:65 notebook/templates/notebook.html:254
msgid "Kernel"
msgstr "カーネル"
#: notebook/templates/notebook.html:68
msgid "This notebook is read-only"
msgstr "このノートブックは読み取り専用です"
#: notebook/templates/notebook.html:81
msgid "New Notebook"
msgstr "新しいノートブック"
#: notebook/templates/notebook.html:85
msgid "Opens a new window with the Dashboard view"
msgstr "ダッシュボードで新しいウィンドウを開く"
#: notebook/templates/notebook.html:86
msgid "Open..."
msgstr "開く..."
#: notebook/templates/notebook.html:90
msgid "Open a copy of this notebook's contents and start a new kernel"
msgstr "このノートブックの内容の複製を開き新しいカーネルを起動する"
#: notebook/templates/notebook.html:91
msgid "Make a Copy..."
msgstr "コピーを作る..."
#: notebook/templates/notebook.html:92
msgid "Rename..."
msgstr "リネーム..."
msgid "Save as..."
msgstr "名前を付けて保存..."
msgid "Quit"
msgstr "終了"
#: notebook/templates/notebook.html:93
msgid "Save and Checkpoint"
msgstr "保存とチェックポイント"
#: notebook/templates/notebook.html:96
msgid "Revert to Checkpoint"
msgstr "チェックポイントを元に戻す"
#: notebook/templates/notebook.html:106
msgid "Print Preview"
msgstr "印刷プレビュー"
#: notebook/templates/notebook.html:107
msgid "Download as"
msgstr "名前を付けてダウンロード"
#: notebook/templates/notebook.html:109
msgid "Notebook (.ipynb)"
msgstr "ノートブック (.ipynb)"
#: notebook/templates/notebook.html:110
msgid "Script"
msgstr "スクリプト"
#: notebook/templates/notebook.html:111
msgid "HTML (.html)"
msgstr ""
#: notebook/templates/notebook.html:112
msgid "Markdown (.md)"
msgstr ""
#: notebook/templates/notebook.html:113
msgid "reST (.rst)"
msgstr ""
#: notebook/templates/notebook.html:114
msgid "LaTeX (.tex)"
msgstr ""
#: notebook/templates/notebook.html:115
msgid "PDF via LaTeX (.pdf)"
msgstr ""
#: notebook/templates/notebook.html:118
msgid "Deploy as"
msgstr "名前を付けてデプロイ"
#: notebook/templates/notebook.html:123
msgid "Trust the output of this notebook"
msgstr "このノートブックの出力を信頼する"
#: notebook/templates/notebook.html:124
msgid "Trust Notebook"
msgstr "ノートブックを信頼する"
#: notebook/templates/notebook.html:127
msgid "Shutdown this notebook's kernel, and close this window"
msgstr "このノートブックのカーネルをシャットダウンし、このウィンドウを閉じる"
#: notebook/templates/notebook.html:128
msgid "Close and Halt"
msgstr "閉じて終了"
#: notebook/templates/notebook.html:133
msgid "Cut Cells"
msgstr "セルを切り取り"
#: notebook/templates/notebook.html:134
msgid "Copy Cells"
msgstr "セルをコピー"
#: notebook/templates/notebook.html:135
msgid "Paste Cells Above"
msgstr "上にセルをペースト"
#: notebook/templates/notebook.html:136
msgid "Paste Cells Below"
msgstr "下にセルをペースト"
#: notebook/templates/notebook.html:137
msgid "Paste Cells &amp; Replace"
msgstr "セルをペーストして入れ替え(&A)"
#: notebook/templates/notebook.html:138
msgid "Delete Cells"
msgstr "セルを削除"
#: notebook/templates/notebook.html:139
msgid "Undo Delete Cells"
msgstr "セルの削除を取り消し"
#: notebook/templates/notebook.html:141
msgid "Split Cell"
msgstr "セルを分割"
#: notebook/templates/notebook.html:142
msgid "Merge Cell Above"
msgstr "上のセルをマージ"
#: notebook/templates/notebook.html:143
msgid "Merge Cell Below"
msgstr "下のセルをマージ"
#: notebook/templates/notebook.html:145
msgid "Move Cell Up"
msgstr "セルを上に移動"
#: notebook/templates/notebook.html:146
msgid "Move Cell Down"
msgstr "セルを下に移動"
#: notebook/templates/notebook.html:148
msgid "Edit Notebook Metadata"
msgstr "ノートブックのメタデータを編集"
#: notebook/templates/notebook.html:150
msgid "Find and Replace"
msgstr "検索と置換"
#: notebook/templates/notebook.html:152
msgid "Cut Cell Attachments"
msgstr "セルのアタッチメントを切り取り"
#: notebook/templates/notebook.html:153
msgid "Copy Cell Attachments"
msgstr "セルのアタッチメントをコピー"
#: notebook/templates/notebook.html:154
msgid "Paste Cell Attachments"
msgstr "セルのアタッチメントをペースト"
#: notebook/templates/notebook.html:156
msgid "Insert Image"
msgstr "画像を挿入"
#: notebook/templates/notebook.html:166
msgid "Show/Hide the action icons (below menu bar)"
msgstr "アクションアイコンを表示/非表示 (メニューバーの下)"
#: notebook/templates/notebook.html:167
msgid "Toggle Toolbar"
msgstr "ツールバーをトグル"
#: notebook/templates/notebook.html:170
msgid "Show/Hide line numbers in cells"
msgstr "セル内の行番号を表示/非表示"
#: notebook/templates/notebook.html:174
msgid "Cell Toolbar"
msgstr "セルツールバー"
#: notebook/templates/notebook.html:179
msgid "Insert"
msgstr "挿入"
#: notebook/templates/notebook.html:182
msgid "Insert an empty Code cell above the currently active cell"
msgstr "現在アクティブなセルの上に空のコードセルを挿入する"
#: notebook/templates/notebook.html:183
msgid "Insert Cell Above"
msgstr "上にセルを挿入"
#: notebook/templates/notebook.html:185
msgid "Insert an empty Code cell below the currently active cell"
msgstr "現在アクティブなセルの下に空のコードセルを挿入する"
#: notebook/templates/notebook.html:186
msgid "Insert Cell Below"
msgstr "下にセルを挿入"
#: notebook/templates/notebook.html:189
msgid "Cell"
msgstr "セル"
#: notebook/templates/notebook.html:191
msgid "Run this cell, and move cursor to the next one"
msgstr "このセルを実行しカーソルを一つ次に移動する"
#: notebook/templates/notebook.html:192
msgid "Run Cells"
msgstr "セルを実行"
#: notebook/templates/notebook.html:193
msgid "Run this cell, select below"
msgstr "このセルを実行し下を選択する"
#: notebook/templates/notebook.html:194
msgid "Run Cells and Select Below"
msgstr "ここまでのセルを実行し下を選択する"
#: notebook/templates/notebook.html:195
msgid "Run this cell, insert below"
msgstr "このセルを実行し下に挿入"
#: notebook/templates/notebook.html:196
msgid "Run Cells and Insert Below"
msgstr "ここまでのセルを実行し下に挿入"
#: notebook/templates/notebook.html:197
msgid "Run all cells in the notebook"
msgstr "ノートブックの全てのセルを実行"
#: notebook/templates/notebook.html:198
msgid "Run All"
msgstr "全てを実行"
#: notebook/templates/notebook.html:199
msgid "Run all cells above (but not including) this cell"
msgstr "このセルの上にある (このセルは含まない) すべてのセルを実行する"
#: notebook/templates/notebook.html:200
msgid "Run All Above"
msgstr "ここまでのセルの全てを実行"
#: notebook/templates/notebook.html:201
msgid "Run this cell and all cells below it"
msgstr "このセルと以下のすべてのセルを実行"
#: notebook/templates/notebook.html:202
msgid "Run All Below"
msgstr "以下を全て実行"
#: notebook/templates/notebook.html:205
msgid "All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells"
msgstr "ノートブック上のすべてのセルには種別があります。デフォルトでは新しいセルは 'コード' セルとして作成されます"
#: notebook/templates/notebook.html:206
msgid "Cell Type"
msgstr "セルの種別"
#: notebook/templates/notebook.html:209
msgid "Contents will be sent to the kernel for execution, and output will display in the footer of cell"
msgstr "実行のために内容がカーネルに送られ、セルのフッターに出力が表示されます"
#: notebook/templates/notebook.html:212
msgid "Contents will be rendered as HTML and serve as explanatory text"
msgstr "内容は HTML としてレンダリングされ説明のテキストとしてサーブされます"
#: notebook/templates/notebook.html:213 notebook/templates/notebook.html:298
msgid "Markdown"
msgstr ""
#: notebook/templates/notebook.html:215
msgid "Contents will pass through nbconvert unmodified"
msgstr "内容は変更されずに nbconvert に渡されます"
#: notebook/templates/notebook.html:216
msgid "Raw NBConvert"
msgstr ""
#: notebook/templates/notebook.html:220
msgid "Current Outputs"
msgstr "現在の出力"
#: notebook/templates/notebook.html:223
msgid "Hide/Show the output of the current cell"
msgstr "現在のセルの出力を表示/非表示"
#: notebook/templates/notebook.html:224 notebook/templates/notebook.html:240
msgid "Toggle"
msgstr "トグル"
#: notebook/templates/notebook.html:227
msgid "Scroll the output of the current cell"
msgstr "現在のセルの出力をスクロール"
#: notebook/templates/notebook.html:228 notebook/templates/notebook.html:244
msgid "Toggle Scrolling"
msgstr "スクロールをトグル"
#: notebook/templates/notebook.html:231
msgid "Clear the output of the current cell"
msgstr "現在のセルの出力をクリア"
#: notebook/templates/notebook.html:232 notebook/templates/notebook.html:248
msgid "Clear"
msgstr "クリア"
#: notebook/templates/notebook.html:236
msgid "All Output"
msgstr "全ての出力"
#: notebook/templates/notebook.html:239
msgid "Hide/Show the output of all cells"
msgstr "全てのセルの出力を表示/非表示"
#: notebook/templates/notebook.html:243
msgid "Scroll the output of all cells"
msgstr "全てのセルの出力をスクロール"
#: notebook/templates/notebook.html:247
msgid "Clear the output of all cells"
msgstr "全てのセルの出力をクリア"
#: notebook/templates/notebook.html:257
msgid "Send Keyboard Interrupt (CTRL-C) to the Kernel"
msgstr "キーボードの中断(CTRL-C)をカーネルに送る"
#: notebook/templates/notebook.html:258
msgid "Interrupt"
msgstr "中断"
#: notebook/templates/notebook.html:261
msgid "Restart the Kernel"
msgstr "カーネルを再起動"
#: notebook/templates/notebook.html:262
msgid "Restart"
msgstr "再起動"
#: notebook/templates/notebook.html:265
msgid "Restart the Kernel and clear all output"
msgstr "カーネルを再起動し全ての出力をクリアする"
#: notebook/templates/notebook.html:266
msgid "Restart &amp; Clear Output"
msgstr "再起動し出力をクリアする"
#: notebook/templates/notebook.html:269
msgid "Restart the Kernel and re-run the notebook"
msgstr "カーネルを再起動しノートブックを再実行する"
#: notebook/templates/notebook.html:270
msgid "Restart &amp; Run All"
msgstr "再起動し全てを実行"
#: notebook/templates/notebook.html:273
msgid "Reconnect to the Kernel"
msgstr "カーネルに再接続する"
#: notebook/templates/notebook.html:274
msgid "Reconnect"
msgstr "再接続"
#: notebook/templates/notebook.html:282
msgid "Change kernel"
msgstr "カーネルの変更"
#: notebook/templates/notebook.html:287
msgid "Help"
msgstr "ヘルプ"
#: notebook/templates/notebook.html:290
msgid "A quick tour of the notebook user interface"
msgstr "ノートブックユーザーインターフェースのクイックツアー"
#: notebook/templates/notebook.html:290
msgid "User Interface Tour"
msgstr "ユーザーインタフェースツアー"
#: notebook/templates/notebook.html:291
msgid "Opens a tooltip with all keyboard shortcuts"
msgstr "全てのキーボードショートカットのツールチップを表示する"
#: notebook/templates/notebook.html:291
msgid "Keyboard Shortcuts"
msgstr "キーボードショートカット"
#: notebook/templates/notebook.html:292
msgid "Opens a dialog allowing you to edit Keyboard shortcuts"
msgstr "キーボードショートカットの編集ダイアログを開く"
#: notebook/templates/notebook.html:292
msgid "Edit Keyboard Shortcuts"
msgstr "キーボードショートカットの編集"
#: notebook/templates/notebook.html:297
msgid "Notebook Help"
msgstr "ノートブックのヘルプ"
#: notebook/templates/notebook.html:303
msgid "Opens in a new window"
msgstr "新しいウィンドウで開く"
#: notebook/templates/notebook.html:319
msgid "About Jupyter Notebook"
msgstr "Jupyter Notebook について"
#: notebook/templates/notebook.html:319
msgid "About"
msgstr "詳細"
#: notebook/templates/page.html:114
msgid "Jupyter Notebook requires JavaScript."
msgstr "Jupyter Notebook には JavaScript が必要です。"
#: notebook/templates/page.html:115
msgid "Please enable it to proceed. "
msgstr "続行するには有効にして下さい。 "
#: notebook/templates/page.html:121
msgid "dashboard"
msgstr "ダッシュボード"
#: notebook/templates/page.html:132
msgid "Logout"
msgstr "ログアウト"
#: notebook/templates/page.html:134
msgid "Login"
msgstr "ログイン"
#: notebook/templates/tree.html:23
msgid "Files"
msgstr "ファイル"
#: notebook/templates/tree.html:24
msgid "Running"
msgstr "実行中"
#: notebook/templates/tree.html:25
msgid "Clusters"
msgstr "クラスタ"
#: notebook/templates/tree.html:32
msgid "Select items to perform actions on them."
msgstr "アクションを実行する為のアイテムを選択して下さい。"
#: notebook/templates/tree.html:35
msgid "Duplicate selected"
msgstr "選択アイテムを複製する"
#: notebook/templates/tree.html:35
msgid "Duplicate"
msgstr "複製"
#: notebook/templates/tree.html:36
msgid "Rename selected"
msgstr "選択アイテムをリネームする"
#: notebook/templates/tree.html:37
msgid "Move selected"
msgstr "選択アイテムを移動する"
#: notebook/templates/tree.html:37
msgid "Move"
msgstr "移動"
#: notebook/templates/tree.html:38
msgid "Download selected"
msgstr "選択アイテムをダウンロードする"
#: notebook/templates/tree.html:39
msgid "Shutdown selected notebook(s)"
msgstr "選択されているノートブックをシャットダウンする"
#: notebook/templates/notebook.html:278
#: notebook/templates/tree.html:39
msgid "Shutdown"
msgstr "シャットダウン"
#: notebook/templates/tree.html:40
msgid "View selected"
msgstr "選択されているアイテムを表示する"
#: notebook/templates/tree.html:41
msgid "Edit selected"
msgstr "選択されているアイテムを編集する"
#: notebook/templates/tree.html:42
msgid "Delete selected"
msgstr "選択されているアイテムを削除する"
#: notebook/templates/tree.html:50
msgid "Click to browse for a file to upload."
msgstr "クリックしてアップロードするファイルを選択して下さい。"
#: notebook/templates/tree.html:51
msgid "Upload"
msgstr "アップロード"
#: notebook/templates/tree.html:65
msgid "Text File"
msgstr "テキストファイル"
#: notebook/templates/tree.html:68
msgid "Folder"
msgstr "フォルダ"
#: notebook/templates/tree.html:72
msgid "Terminal"
msgstr "端末"
#: notebook/templates/tree.html:76
msgid "Terminals Unavailable"
msgstr "端末が存在しません"
#: notebook/templates/tree.html:82
msgid "Refresh notebook list"
msgstr "ノートブックの一覧を再読み込み"
#: notebook/templates/tree.html:90
msgid "Select All / None"
msgstr "全てを選択 / 解除"
#: notebook/templates/tree.html:93
msgid "Select..."
msgstr "選択..."
#: notebook/templates/tree.html:98
msgid "Select All Folders"
msgstr "全てのフォルダを選択..."
#: notebook/templates/tree.html:98
msgid "Folders"
msgstr "フォルダ"
#: notebook/templates/tree.html:99
msgid "Select All Notebooks"
msgstr "全てのノートブックを選択"
#: notebook/templates/tree.html:99
msgid "All Notebooks"
msgstr "全てのノートブック"
#: notebook/templates/tree.html:100
msgid "Select Running Notebooks"
msgstr "実行中のノートブックを選択"
#: notebook/templates/tree.html:100
msgid "Running"
msgstr "実行中"
#: notebook/templates/tree.html:101
msgid "Select All Files"
msgstr "全てのファイルを選択"
#: notebook/templates/tree.html:101
msgid "Files"
msgstr "ファイル"
#: notebook/templates/tree.html:114
msgid "Last Modified"
msgstr "最終変更時刻"
#: notebook/templates/tree.html:120
msgid "Name"
msgstr "名前"
msgid "File size"
msgstr "ファイルサイズ"
#: notebook/templates/tree.html:130
msgid "Currently running Jupyter processes"
msgstr "現在実行中の Jupyter プロセス一覧"
#: notebook/templates/tree.html:134
msgid "Refresh running list"
msgstr "実行中の一覧を再読み込み"
#: notebook/templates/tree.html:150
msgid "There are no terminals running."
msgstr "実行中の端末はありません。"
#: notebook/templates/tree.html:152
msgid "Terminals are unavailable."
msgstr "端末はありません。"
#: notebook/templates/tree.html:162
msgid "Notebooks"
msgstr "ノートブック"
#: notebook/templates/tree.html:169
msgid "There are no notebooks running."
msgstr "実行中のノートブックはありません。"
#: notebook/templates/tree.html:178
msgid "Clusters tab is now provided by IPython parallel."
msgstr "Clousters タブが IPython parallel によって提供される様になりました。"
#: notebook/templates/tree.html:179
msgid "See '<a href=\"https://github.com/ipython/ipyparallel\">IPython parallel</a>' for installation details."
msgstr "詳しいインストール方法は '<a href=\"https://github.com/ipython/ipyparallel\">IPython parallel</a>' を参照"

@ -1,527 +0,0 @@
# Translations template for Jupyter.
# Copyright (C) 2017 ORGANIZATION
# This file is distributed under the same license as the Jupyter project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Jupyter VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2017-07-08 21:52-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
#: notebook/notebookapp.py:53
msgid "The Jupyter Notebook requires tornado >= 4.0"
msgstr "Jupyter Notebook は tornade 4.0 以上が必要です"
#: notebook/notebookapp.py:57
msgid "The Jupyter Notebook requires tornado >= 4.0, but you have < 1.1.0"
msgstr "Jupyter Notebook は tornade 4.0 以上が必要ですが 1.1.0 以下です"
#: notebook/notebookapp.py:59
#, python-format
msgid "The Jupyter Notebook requires tornado >= 4.0, but you have %s"
msgstr "Jupyter Notebook は tornade 4.0 以上が必要ですが %s です"
#: notebook/notebookapp.py:209
msgid "The `ignore_minified_js` flag is deprecated and no longer works."
msgstr "`ignore_minified_js` フラグは非推奨であり既に動作していません。"
#: notebook/notebookapp.py:210
#, python-format
msgid "Alternatively use `%s` when working on the notebook's Javascript and LESS"
msgstr "ノートブックの Javascript と LESS で動作する場合には代わりに `%s` を使用してください。"
#: notebook/notebookapp.py:211
msgid "The `ignore_minified_js` flag is deprecated and will be removed in Notebook 6.0"
msgstr "`ignore_minified_js` フラグは非推奨でありノートブック 6.0 では削除されます"
#: notebook/notebookapp.py:389
msgid "List currently running notebook servers."
msgstr "現在起動中のノートブックサーバの一覧"
#: notebook/notebookapp.py:393
msgid "Produce machine-readable JSON output."
msgstr "機械で読み込み可能な JSON 出力。"
#: notebook/notebookapp.py:397
msgid "If True, each line of output will be a JSON object with the details from the server info file."
msgstr "True の場合、出力の各行はサーバ情報ファイルからの詳細情報を含む JSON オブジェクトになります。"
#: notebook/notebookapp.py:402
msgid "Currently running servers:"
msgstr "現在実行中のサーバ:"
#: notebook/notebookapp.py:419
msgid "Don't open the notebook in a browser after startup."
msgstr "起動後にブラウザでノートブックを開かない。"
#: notebook/notebookapp.py:423
msgid "DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib."
msgstr "無効: matplotlib を有効にするにはノートブックで %pylab または %matplotlib を使用して下さい。"
#: notebook/notebookapp.py:439
msgid "Allow the notebook to be run from root user."
msgstr "ートブックをrootユーザーから実行できるようにする。"
#: notebook/notebookapp.py:470
msgid ""
"The Jupyter HTML Notebook.\n"
" \n"
" This launches a Tornado based HTML Notebook Server that serves up an HTML5/Javascript Notebook client."
msgstr ""
"The Jupyter HTML Notebook.\n"
" \n"
" HTML5/Javascript Notebook クライアントを提供する Tornado ベースの HTML Notebook サーバを起動します。"
#: notebook/notebookapp.py:509
msgid "Deprecated: Use minified JS file or not, mainly use during dev to avoid JS recompilation"
msgstr "非推奨: 圧縮された JS ファイルを使用するかどうか。主に開発中に JS が再コンパイルされるのを回避するために使用します。"
#: notebook/notebookapp.py:540
msgid "Set the Access-Control-Allow-Credentials: true header"
msgstr "Access-Control-Allow-Credentials: true ヘッダーを設定します。"
#: notebook/notebookapp.py:544
msgid "Whether to allow the user to run the notebook as root."
msgstr "ユーザーがノートブックを root として実行できるようにするかどうか。"
#: notebook/notebookapp.py:548
msgid "The default URL to redirect to from `/`"
msgstr "`/` からリダイレクトされるデフォルトの URL"
#: notebook/notebookapp.py:552
msgid "The IP address the notebook server will listen on."
msgstr "ノートブックサーバが待ち受ける IP アドレス。"
#: notebook/notebookapp.py:565
#, python-format
msgid ""
"Cannot bind to localhost, using 127.0.0.1 as default ip\n"
"%s"
msgstr ""
"localhost でバインドできません。デフォルト IP アドレスとして 127.0.0.1 を使用します\n"
"%s"
#: notebook/notebookapp.py:579
msgid "The port the notebook server will listen on."
msgstr "ノートブックサーバが待ち受けするポート番号。"
#: notebook/notebookapp.py:583
msgid "The number of additional ports to try if the specified port is not available."
msgstr "指定されたポートが利用できない場合に試す追加のポートの数。"
#: notebook/notebookapp.py:587
msgid "The full path to an SSL/TLS certificate file."
msgstr "SSL/TLS 証明書ファイルへの完全なパス。"
#: notebook/notebookapp.py:591
msgid "The full path to a private key file for usage with SSL/TLS."
msgstr "SSL/TLS で使用する秘密鍵ファイルへの完全なパス。"
#: notebook/notebookapp.py:595
msgid "The full path to a certificate authority certificate for SSL/TLS client authentication."
msgstr "SSL/TLS クライアント認証用の認証局証明書への完全なパス。"
#: notebook/notebookapp.py:599
msgid "The file where the cookie secret is stored."
msgstr "cookie secret を保存するファイル。"
#: notebook/notebookapp.py:628
#, python-format
msgid "Writing notebook server cookie secret to %s"
msgstr "ノートブックサーバは cookie secret を %s に書き込みます"
#: notebook/notebookapp.py:635
#, python-format
msgid "Could not set permissions on %s"
msgstr "%s の権限を設定出来ませんでした"
#: notebook/notebookapp.py:640
msgid ""
"Token used for authenticating first-time connections to the server.\n"
"\n"
" When no password is enabled,\n"
" the default is to generate a new, random token.\n"
"\n"
" Setting to an empty string disables authentication altogether, which is NOT RECOMMENDED.\n"
" "
msgstr ""
"サーバに接続する際に初回の認証に使われるトークン。\n"
"\n"
" パスワード無しが有効になっている場合\n"
" デフォルト値はランダムなトークンが新しく生成されます。\n"
"\n"
" 空の文字列に設定すると認証が完全に無効になります。これは推奨されていません。\n"
" "
#: notebook/notebookapp.py:650
msgid ""
"One-time token used for opening a browser.\n"
" Once used, this token cannot be used again.\n"
" "
msgstr ""
"開いたブラウザが仕様するワンタイムトークン。\n"
" 1度使用されると再度使用する事が出来ません。\n"
" "
#: notebook/notebookapp.py:726
msgid ""
"Specify Where to open the notebook on startup. This is the\n"
" `new` argument passed to the standard library method `webbrowser.open`.\n"
" The behaviour is not guaranteed, but depends on browser support. Valid\n"
" values are:\n"
" 2 opens a new tab,\n"
" 1 opens a new window,\n"
" 0 opens in an existing window.\n"
" See the `webbrowser.open` documentation for details.\n"
" "
msgstr ""
"起動時にどこでノートブックを開くかを指定します。これは\n"
" 標準ライブラリのメソッド `webbrowser.open` の引数 `new` に渡されます。\n"
" 動作は保証されていませんがブラウザのサポートによって異なります。\n"
" 有効な値:\n"
" 2 新しいタブで開く\n"
" 1 新しいウィンドウで開く\n"
" 0 既にあるウィンドウで開く\n"
" 詳細は `webbrowser.open` のドキュメントを参照。\n"
" "
#: notebook/notebookapp.py:737
msgid "DEPRECATED, use tornado_settings"
msgstr "非推奨 tornado_settings の使用"
#: notebook/notebookapp.py:742
msgid ""
"\n"
" webapp_settings is deprecated, use tornado_settings.\n"
msgstr ""
"\n"
" webapp_settings は非推奨です。tornado_settings を使って下さい。\n"
#: notebook/notebookapp.py:746
msgid "Supply overrides for the tornado.web.Application that the Jupyter notebook uses."
msgstr "Jupyterートブックが使用する tornado.web.Application のオーバーライドを指定します。"
#: notebook/notebookapp.py:750
msgid ""
"\n"
" Set the tornado compression options for websocket connections.\n"
"\n"
" This value will be returned from :meth:`WebSocketHandler.get_compression_options`.\n"
" None (default) will disable compression.\n"
" A dict (even an empty one) will enable compression.\n"
"\n"
" See the tornado docs for WebSocketHandler.get_compression_options for details.\n"
" "
msgstr ""
"\n"
" tornado の websocket 接続の圧縮オプションを指定します。\n"
"\n"
" この値は :meth:`WebSocketHandler.get_compression_options` から返されます。\n"
" None (default) の場合は圧縮は無効になります。\n"
" 辞書 (空でも良い) の場合は圧縮が有効になります。\n"
"\n"
" 詳細は tornado の WebSocketHandler.get_compression_options のドキュメントを参照。\n"
" "
#: notebook/notebookapp.py:761
msgid "Supply overrides for terminado. Currently only supports \"shell_command\"."
msgstr "terminado のオーバーライドを指定します。現時は \"shell_command \" のみをサポートしています。"
#: notebook/notebookapp.py:764
msgid "Extra keyword arguments to pass to `set_secure_cookie`. See tornado's set_secure_cookie docs for details."
msgstr "`set_secure_cookie` に渡す追加のキーワード引数。詳細は tornado の set_secure_cookie のドキュメントを参照。"
#: notebook/notebookapp.py:768
msgid ""
"Supply SSL options for the tornado HTTPServer.\n"
" See the tornado docs for details."
msgstr ""
"tornado HTTPServer の SSL オプションを指定します。\n"
" 詳しくは tornado のドキュメントを参照。"
#: notebook/notebookapp.py:772
msgid "Supply extra arguments that will be passed to Jinja environment."
msgstr "Jinja environment に渡される追加の引数を指定します。"
#: notebook/notebookapp.py:776
msgid "Extra variables to supply to jinja templates when rendering."
msgstr "jinja テンプレートがレンダリングする際に渡される追加の変数。"
#: notebook/notebookapp.py:812
msgid "DEPRECATED use base_url"
msgstr "非推奨 base_url の使用"
#: notebook/notebookapp.py:816
msgid "base_project_url is deprecated, use base_url"
msgstr "base_project_url は非推奨です。base_url を使用して下さい。"
#: notebook/notebookapp.py:832
msgid "Path to search for custom.js, css"
msgstr "custom.js、CSS を検索するためのパス"
#: notebook/notebookapp.py:844
msgid ""
"Extra paths to search for serving jinja templates.\n"
"\n"
" Can be used to override templates from notebook.templates."
msgstr ""
"Jinja テンプレートを探す為の追加パス。\n"
"\n"
" notebook.templates を上書きする為に使う事が出来ます。"
#: notebook/notebookapp.py:855
msgid "extra paths to look for Javascript notebook extensions"
msgstr "Javascript ノートブック拡張への追加パス"
#: notebook/notebookapp.py:900
#, python-format
msgid "Using MathJax: %s"
msgstr "使用している MathJax: %s"
#: notebook/notebookapp.py:903
msgid "The MathJax.js configuration file that is to be used."
msgstr "使用される MathJax.js 設定ファイル。"
#: notebook/notebookapp.py:908
#, python-format
msgid "Using MathJax configuration file: %s"
msgstr "使用する MathJax 設定ファイル: %s"
#: notebook/notebookapp.py:914
msgid "The notebook manager class to use."
msgstr "ノートブックマネージャのクラス"
#: notebook/notebookapp.py:920
msgid "The kernel manager class to use."
msgstr "カーネルマネージャのクラス"
#: notebook/notebookapp.py:926
msgid "The session manager class to use."
msgstr "セッションマネージャのクラス"
#: notebook/notebookapp.py:932
msgid "The config manager class to use"
msgstr "設定マネージャのクラス"
#: notebook/notebookapp.py:953
msgid "The login handler class to use."
msgstr "ログインのハンドラクラス"
#: notebook/notebookapp.py:960
msgid "The logout handler class to use."
msgstr "ログアウトのハンドラクラス"
#: notebook/notebookapp.py:964
msgid "Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headerssent by the upstream reverse proxy. Necessary if the proxy handles SSL"
msgstr "X-Scheme/X-Forwarded-Proto および X-Real-Ip/X-Forwarded-For ヘッダーがアップストリームのリバースプロキシによって送信されたことを信頼するかどうか。プロキシが SSL を処理する場合に必要となります。"
#: notebook/notebookapp.py:976
msgid ""
"\n"
" DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib.\n"
" "
msgstr ""
"\n"
" 非推奨: matplotlib を有効にするにはノートブックで %pylab または %matplotlib\n"
" を実行して下さい。"
#: notebook/notebookapp.py:988
msgid "Support for specifying --pylab on the command line has been removed."
msgstr "コマンドラインでの --pylab 指定はサポートされなくなりました。"
#: notebook/notebookapp.py:990
msgid "Please use `%pylab{0}` or `%matplotlib{0}` in the notebook itself."
msgstr "ノートブックの中で `%pylab{0}` または `%matplotlib{0}` を使ってください。"
#: notebook/notebookapp.py:995
msgid "The directory to use for notebooks and kernels."
msgstr "ノートブックとカーネルが使うディレクトリ。"
#: notebook/notebookapp.py:1018
#, python-format
msgid "No such notebook dir: '%r'"
msgstr "ノートブックディレクトリが見つかりません: '%r'"
#: notebook/notebookapp.py:1031
msgid "DEPRECATED use the nbserver_extensions dict instead"
msgstr "非推奨 nbserver_extensions 辞書を代わりに使用して下さい "
#: notebook/notebookapp.py:1036
msgid "server_extensions is deprecated, use nbserver_extensions"
msgstr "server_extensions が非推奨です。 nbserver_extensions を使用して下さい。"
#: notebook/notebookapp.py:1040
msgid "Dict of Python modules to load as notebook server extensions.Entry values can be used to enable and disable the loading ofthe extensions. The extensions will be loaded in alphabetical order."
msgstr "ノートブックサーバ拡張としてロードする Python モジュールの辞書。エントリー値を使用して拡張のロードを有効または無効にすることができます。 拡張子はアルファベット順にロードされます。"
#: notebook/notebookapp.py:1049
msgid "Reraise exceptions encountered loading server extensions?"
msgstr "サーバ拡張の読み込み中に例外が発生しましたか?"
#: notebook/notebookapp.py:1052
msgid ""
"(msgs/sec)\n"
" Maximum rate at which messages can be sent on iopub before they are\n"
" limited."
msgstr "メッセージが送信される前に iopub で送信可能な最大レート。"
#: notebook/notebookapp.py:1056
msgid ""
"(bytes/sec)\n"
" Maximum rate at which stream output can be sent on iopub before they are\n"
" limited."
msgstr ""
"(bytes/sec)\n"
" ストリーム出力が送信制限される前に iopub で送信可能な最大レート。"
#: notebook/notebookapp.py:1060
msgid ""
"(sec) Time window used to \n"
" check the message and data rate limits."
msgstr ""
"(sec) このウィンドウはメッセージとデータの帯域リミット\n"
" をチェックする為に使用されます。"
#: notebook/notebookapp.py:1071
#, python-format
msgid "No such file or directory: %s"
msgstr "その様なファイルまたはディレクトリは存在しません: %s"
#: notebook/notebookapp.py:1141
msgid "Notebook servers are configured to only be run with a password."
msgstr "ノートブックサーバはパスワードが設定された場合にだけ動作するよう設定されています。"
#: notebook/notebookapp.py:1142
msgid "Hint: run the following command to set a password"
msgstr "ヒント: パスワードを設定するには以下のコマンドを実行します"
#: notebook/notebookapp.py:1143
msgid "\t$ python -m notebook.auth password"
msgstr ""
#: notebook/notebookapp.py:1181
#, python-format
msgid "The port %i is already in use, trying another port."
msgstr "ポート %i は既に使用されています。他のポートで試して下さい。"
#: notebook/notebookapp.py:1184
#, python-format
msgid "Permission to listen on port %i denied"
msgstr "ポート %i で待機する権限がありません"
#: notebook/notebookapp.py:1193
msgid "ERROR: the notebook server could not be started because no available port could be found."
msgstr "エラー: 有効なポートが見付からなかったためノートブックサーバを起動できませんでした。"
#: notebook/notebookapp.py:1199
msgid "[all ip addresses on your system]"
msgstr "[システム上の全ての IP アドレス]"
#: notebook/notebookapp.py:1223
#, python-format
msgid "Terminals not available (error was %s)"
msgstr "端末は存在しません (%s でエラー発生)"
#: notebook/notebookapp.py:1259
msgid "interrupted"
msgstr "中断しました"
#: notebook/notebookapp.py:1261
msgid "y"
msgstr ""
#: notebook/notebookapp.py:1262
msgid "n"
msgstr ""
#: notebook/notebookapp.py:1263
#, python-format
msgid "Shutdown this notebook server (%s/[%s])? "
msgstr "このノートブックサーバをシャットダウンしますか? (%s/[%s])"
#: notebook/notebookapp.py:1269
msgid "Shutdown confirmed"
msgstr "シャットダウンの確認"
#: notebook/notebookapp.py:1273
msgid "No answer for 5s:"
msgstr "5秒間に応答がありません:"
#: notebook/notebookapp.py:1274
msgid "resuming operation..."
msgstr "操作を再開中..."
#: notebook/notebookapp.py:1282
#, python-format
msgid "received signal %s, stopping"
msgstr "シグナル %s を受信。停止します"
#: notebook/notebookapp.py:1338
#, python-format
msgid "Error loading server extension %s"
msgstr "サーバ拡張 %s の読み込みエラー"
#: notebook/notebookapp.py:1369
#, python-format
msgid "Shutting down %d kernels"
msgstr "%d 個のカーネルをシャットダウンしています"
#: notebook/notebookapp.py:1375
#, python-format
msgid "%d active kernel"
msgid_plural "%d active kernels"
msgstr[0] "%d 個のアクティブなカーネル"
msgstr[1] "%d 個のアクティブなカーネル"
#: notebook/notebookapp.py:1379
#, python-format
msgid ""
"The Jupyter Notebook is running at:\n"
"%s"
msgstr ""
"Jupyter Notebook は以下の URL 起動しています:\n"
"%s"
#: notebook/notebookapp.py:1426
msgid "Running as root is not recommended. Use --allow-root to bypass."
msgstr "root ユーザでの実行は推奨されません。バイパスするには --allow-root を使って下さい。"
#: notebook/notebookapp.py:1432
msgid "Use Control-C to stop this server and shut down all kernels (twice to skip confirmation)."
msgstr "サーバを停止し全てのカーネルをシャットダウンするには Control-C を使って下さい(確認をスキップするには2回)。"
#: notebook/notebookapp.py:1434
msgid "Welcome to Project Jupyter! Explore the various tools available and their corresponding documentation. If you are interested in contributing to the platform, please visit the communityresources section at http://jupyter.org/community.html."
msgstr "Project Jupyter へようこそ! 利用可能な色々なツールとそれに対応するドキュメントを探索して下さい。プラットフォームへの貢献に興味がある場合は http://jupyter.org/community.html の communityresources セクションにアクセスしてください。"
#: notebook/notebookapp.py:1445
#, python-format
msgid "No web browser found: %s."
msgstr "ウェブブラウザが見つかりません: %s"
#: notebook/notebookapp.py:1450
#, python-format
msgid "%s does not exist"
msgstr "%s は存在しません"
#: notebook/notebookapp.py:1484
msgid "Interrupted..."
msgstr "中断..."
#: notebook/services/contents/filemanager.py:506
#, python-format
msgid "Serving notebooks from local directory: %s"
msgstr "ローカルディレクトリからノートブックをサーブ: %s"
#: notebook/services/contents/manager.py:68
msgid "Untitled"
msgstr ""

@ -1,16 +0,0 @@
{
"domain": "nbjs",
"supported_languages": [
"fr-FR",
"zh-CN",
"nl",
"ja_JP"
],
"locale_data": {
"nbjs": {
"": {
"domain": "nbjs"
}
}
}
}

File diff suppressed because it is too large Load Diff

@ -1,732 +0,0 @@
# Translations template for Jupyter.
# Copyright (C) 2017 ORGANIZATION
# This file is distributed under the same license as the Jupyter project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Jupyter VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2017-07-07 12:48-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
#: notebook/templates/404.html:3
msgid "You are requesting a page that does not exist!"
msgstr ""
#: notebook/templates/edit.html:37
msgid "current mode"
msgstr ""
#: notebook/templates/edit.html:48 notebook/templates/notebook.html:78
msgid "File"
msgstr ""
#: notebook/templates/edit.html:50 notebook/templates/tree.html:57
msgid "New"
msgstr ""
#: notebook/templates/edit.html:51
msgid "Save"
msgstr ""
#: notebook/templates/edit.html:52 notebook/templates/tree.html:36
msgid "Rename"
msgstr ""
#: notebook/templates/edit.html:53 notebook/templates/tree.html:38
msgid "Download"
msgstr ""
#: notebook/templates/edit.html:56 notebook/templates/notebook.html:131
#: notebook/templates/tree.html:41
msgid "Edit"
msgstr ""
#: notebook/templates/edit.html:58
msgid "Find"
msgstr ""
#: notebook/templates/edit.html:59
msgid "Find &amp; Replace"
msgstr ""
#: notebook/templates/edit.html:61
msgid "Key Map"
msgstr ""
#: notebook/templates/edit.html:62
msgid "Default"
msgstr ""
#: notebook/templates/edit.html:63
msgid "Sublime Text"
msgstr ""
#: notebook/templates/edit.html:68 notebook/templates/notebook.html:159
#: notebook/templates/tree.html:40
msgid "View"
msgstr ""
#: notebook/templates/edit.html:70 notebook/templates/notebook.html:162
msgid "Show/Hide the logo and notebook title (above menu bar)"
msgstr ""
#: notebook/templates/edit.html:71 notebook/templates/notebook.html:163
msgid "Toggle Header"
msgstr ""
#: notebook/templates/edit.html:72 notebook/templates/notebook.html:171
msgid "Toggle Line Numbers"
msgstr ""
#: notebook/templates/edit.html:75
msgid "Language"
msgstr ""
#: notebook/templates/error.html:23
msgid "The error was:"
msgstr ""
#: notebook/templates/login.html:24
msgid "Password or token:"
msgstr ""
#: notebook/templates/login.html:26
msgid "Password:"
msgstr ""
#: notebook/templates/login.html:31
msgid "Log in"
msgstr ""
#: notebook/templates/login.html:39
msgid "No login available, you shouldn't be seeing this page."
msgstr ""
#: notebook/templates/logout.html:24
#, python-format
msgid "Proceed to the <a href=\"%(base_url)s\">dashboard"
msgstr ""
#: notebook/templates/logout.html:26
#, python-format
msgid "Proceed to the <a href=\"%(base_url)slogin\">login page"
msgstr ""
#: notebook/templates/notebook.html:62
msgid "Menu"
msgstr ""
#: notebook/templates/notebook.html:65 notebook/templates/notebook.html:254
msgid "Kernel"
msgstr ""
#: notebook/templates/notebook.html:68
msgid "This notebook is read-only"
msgstr ""
#: notebook/templates/notebook.html:81
msgid "New Notebook"
msgstr ""
#: notebook/templates/notebook.html:85
msgid "Opens a new window with the Dashboard view"
msgstr ""
#: notebook/templates/notebook.html:86
msgid "Open..."
msgstr ""
#: notebook/templates/notebook.html:90
msgid "Open a copy of this notebook's contents and start a new kernel"
msgstr ""
#: notebook/templates/notebook.html:91
msgid "Make a Copy..."
msgstr ""
#: notebook/templates/notebook.html:92
msgid "Rename..."
msgstr ""
#: notebook/templates/notebook.html:93
msgid "Save and Checkpoint"
msgstr ""
#: notebook/templates/notebook.html:96
msgid "Revert to Checkpoint"
msgstr ""
#: notebook/templates/notebook.html:106
msgid "Print Preview"
msgstr ""
#: notebook/templates/notebook.html:107
msgid "Download as"
msgstr ""
#: notebook/templates/notebook.html:109
msgid "Notebook (.ipynb)"
msgstr ""
#: notebook/templates/notebook.html:110
msgid "Script"
msgstr ""
#: notebook/templates/notebook.html:111
msgid "HTML (.html)"
msgstr ""
#: notebook/templates/notebook.html:112
msgid "Markdown (.md)"
msgstr ""
#: notebook/templates/notebook.html:113
msgid "reST (.rst)"
msgstr ""
#: notebook/templates/notebook.html:114
msgid "LaTeX (.tex)"
msgstr ""
#: notebook/templates/notebook.html:115
msgid "PDF via LaTeX (.pdf)"
msgstr ""
#: notebook/templates/notebook.html:118
msgid "Deploy as"
msgstr ""
#: notebook/templates/notebook.html:123
msgid "Trust the output of this notebook"
msgstr ""
#: notebook/templates/notebook.html:124
msgid "Trust Notebook"
msgstr ""
#: notebook/templates/notebook.html:127
msgid "Shutdown this notebook's kernel, and close this window"
msgstr ""
#: notebook/templates/notebook.html:128
msgid "Close and Halt"
msgstr ""
#: notebook/templates/notebook.html:133
msgid "Cut Cells"
msgstr ""
#: notebook/templates/notebook.html:134
msgid "Copy Cells"
msgstr ""
#: notebook/templates/notebook.html:135
msgid "Paste Cells Above"
msgstr ""
#: notebook/templates/notebook.html:136
msgid "Paste Cells Below"
msgstr ""
#: notebook/templates/notebook.html:137
msgid "Paste Cells &amp; Replace"
msgstr ""
#: notebook/templates/notebook.html:138
msgid "Delete Cells"
msgstr ""
#: notebook/templates/notebook.html:139
msgid "Undo Delete Cells"
msgstr ""
#: notebook/templates/notebook.html:141
msgid "Split Cell"
msgstr ""
#: notebook/templates/notebook.html:142
msgid "Merge Cell Above"
msgstr ""
#: notebook/templates/notebook.html:143
msgid "Merge Cell Below"
msgstr ""
#: notebook/templates/notebook.html:145
msgid "Move Cell Up"
msgstr ""
#: notebook/templates/notebook.html:146
msgid "Move Cell Down"
msgstr ""
#: notebook/templates/notebook.html:148
msgid "Edit Notebook Metadata"
msgstr ""
#: notebook/templates/notebook.html:150
msgid "Find and Replace"
msgstr ""
#: notebook/templates/notebook.html:152
msgid "Cut Cell Attachments"
msgstr ""
#: notebook/templates/notebook.html:153
msgid "Copy Cell Attachments"
msgstr ""
#: notebook/templates/notebook.html:154
msgid "Paste Cell Attachments"
msgstr ""
#: notebook/templates/notebook.html:156
msgid "Insert Image"
msgstr ""
#: notebook/templates/notebook.html:166
msgid "Show/Hide the action icons (below menu bar)"
msgstr ""
#: notebook/templates/notebook.html:167
msgid "Toggle Toolbar"
msgstr ""
#: notebook/templates/notebook.html:170
msgid "Show/Hide line numbers in cells"
msgstr ""
#: notebook/templates/notebook.html:174
msgid "Cell Toolbar"
msgstr ""
#: notebook/templates/notebook.html:179
msgid "Insert"
msgstr ""
#: notebook/templates/notebook.html:182
msgid "Insert an empty Code cell above the currently active cell"
msgstr ""
#: notebook/templates/notebook.html:183
msgid "Insert Cell Above"
msgstr ""
#: notebook/templates/notebook.html:185
msgid "Insert an empty Code cell below the currently active cell"
msgstr ""
#: notebook/templates/notebook.html:186
msgid "Insert Cell Below"
msgstr ""
#: notebook/templates/notebook.html:189
msgid "Cell"
msgstr ""
#: notebook/templates/notebook.html:191
msgid "Run this cell, and move cursor to the next one"
msgstr ""
#: notebook/templates/notebook.html:192
msgid "Run Cells"
msgstr ""
#: notebook/templates/notebook.html:193
msgid "Run this cell, select below"
msgstr ""
#: notebook/templates/notebook.html:194
msgid "Run Cells and Select Below"
msgstr ""
#: notebook/templates/notebook.html:195
msgid "Run this cell, insert below"
msgstr ""
#: notebook/templates/notebook.html:196
msgid "Run Cells and Insert Below"
msgstr ""
#: notebook/templates/notebook.html:197
msgid "Run all cells in the notebook"
msgstr ""
#: notebook/templates/notebook.html:198
msgid "Run All"
msgstr ""
#: notebook/templates/notebook.html:199
msgid "Run all cells above (but not including) this cell"
msgstr ""
#: notebook/templates/notebook.html:200
msgid "Run All Above"
msgstr ""
#: notebook/templates/notebook.html:201
msgid "Run this cell and all cells below it"
msgstr ""
#: notebook/templates/notebook.html:202
msgid "Run All Below"
msgstr ""
#: notebook/templates/notebook.html:205
msgid "All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells"
msgstr ""
#: notebook/templates/notebook.html:206
msgid "Cell Type"
msgstr ""
#: notebook/templates/notebook.html:209
msgid "Contents will be sent to the kernel for execution, and output will display in the footer of cell"
msgstr ""
#: notebook/templates/notebook.html:212
msgid "Contents will be rendered as HTML and serve as explanatory text"
msgstr ""
#: notebook/templates/notebook.html:213 notebook/templates/notebook.html:298
msgid "Markdown"
msgstr ""
#: notebook/templates/notebook.html:215
msgid "Contents will pass through nbconvert unmodified"
msgstr ""
#: notebook/templates/notebook.html:216
msgid "Raw NBConvert"
msgstr ""
#: notebook/templates/notebook.html:220
msgid "Current Outputs"
msgstr ""
#: notebook/templates/notebook.html:223
msgid "Hide/Show the output of the current cell"
msgstr ""
#: notebook/templates/notebook.html:224 notebook/templates/notebook.html:240
msgid "Toggle"
msgstr ""
#: notebook/templates/notebook.html:227
msgid "Scroll the output of the current cell"
msgstr ""
#: notebook/templates/notebook.html:228 notebook/templates/notebook.html:244
msgid "Toggle Scrolling"
msgstr ""
#: notebook/templates/notebook.html:231
msgid "Clear the output of the current cell"
msgstr ""
#: notebook/templates/notebook.html:232 notebook/templates/notebook.html:248
msgid "Clear"
msgstr ""
#: notebook/templates/notebook.html:236
msgid "All Output"
msgstr ""
#: notebook/templates/notebook.html:239
msgid "Hide/Show the output of all cells"
msgstr ""
#: notebook/templates/notebook.html:243
msgid "Scroll the output of all cells"
msgstr ""
#: notebook/templates/notebook.html:247
msgid "Clear the output of all cells"
msgstr ""
#: notebook/templates/notebook.html:257
msgid "Send Keyboard Interrupt (CTRL-C) to the Kernel"
msgstr ""
#: notebook/templates/notebook.html:258
msgid "Interrupt"
msgstr ""
#: notebook/templates/notebook.html:261
msgid "Restart the Kernel"
msgstr ""
#: notebook/templates/notebook.html:262
msgid "Restart"
msgstr ""
#: notebook/templates/notebook.html:265
msgid "Restart the Kernel and clear all output"
msgstr ""
#: notebook/templates/notebook.html:266
msgid "Restart &amp; Clear Output"
msgstr ""
#: notebook/templates/notebook.html:269
msgid "Restart the Kernel and re-run the notebook"
msgstr ""
#: notebook/templates/notebook.html:270
msgid "Restart &amp; Run All"
msgstr ""
#: notebook/templates/notebook.html:273
msgid "Reconnect to the Kernel"
msgstr ""
#: notebook/templates/notebook.html:274
msgid "Reconnect"
msgstr ""
#: notebook/templates/notebook.html:282
msgid "Change kernel"
msgstr ""
#: notebook/templates/notebook.html:287
msgid "Help"
msgstr ""
#: notebook/templates/notebook.html:290
msgid "A quick tour of the notebook user interface"
msgstr ""
#: notebook/templates/notebook.html:290
msgid "User Interface Tour"
msgstr ""
#: notebook/templates/notebook.html:291
msgid "Opens a tooltip with all keyboard shortcuts"
msgstr ""
#: notebook/templates/notebook.html:291
msgid "Keyboard Shortcuts"
msgstr ""
#: notebook/templates/notebook.html:292
msgid "Opens a dialog allowing you to edit Keyboard shortcuts"
msgstr ""
#: notebook/templates/notebook.html:292
msgid "Edit Keyboard Shortcuts"
msgstr ""
#: notebook/templates/notebook.html:297
msgid "Notebook Help"
msgstr ""
#: notebook/templates/notebook.html:303
msgid "Opens in a new window"
msgstr ""
#: notebook/templates/notebook.html:319
msgid "About Jupyter Notebook"
msgstr ""
#: notebook/templates/notebook.html:319
msgid "About"
msgstr ""
#: notebook/templates/page.html:114
msgid "Jupyter Notebook requires JavaScript."
msgstr ""
#: notebook/templates/page.html:115
msgid "Please enable it to proceed. "
msgstr ""
#: notebook/templates/page.html:121
msgid "dashboard"
msgstr ""
#: notebook/templates/page.html:132
msgid "Logout"
msgstr ""
#: notebook/templates/page.html:134
msgid "Login"
msgstr ""
#: notebook/templates/tree.html:23
msgid "Files"
msgstr ""
#: notebook/templates/tree.html:24
msgid "Running"
msgstr ""
#: notebook/templates/tree.html:25
msgid "Clusters"
msgstr ""
#: notebook/templates/tree.html:32
msgid "Select items to perform actions on them."
msgstr ""
#: notebook/templates/tree.html:35
msgid "Duplicate selected"
msgstr ""
#: notebook/templates/tree.html:35
msgid "Duplicate"
msgstr ""
#: notebook/templates/tree.html:36
msgid "Rename selected"
msgstr ""
#: notebook/templates/tree.html:37
msgid "Move selected"
msgstr ""
#: notebook/templates/tree.html:37
msgid "Move"
msgstr ""
#: notebook/templates/tree.html:38
msgid "Download selected"
msgstr ""
#: notebook/templates/tree.html:39
msgid "Shutdown selected notebook(s)"
msgstr ""
#: notebook/templates/notebook.html:278
#: notebook/templates/tree.html:39
msgid "Shutdown"
msgstr ""
#: notebook/templates/tree.html:40
msgid "View selected"
msgstr ""
#: notebook/templates/tree.html:41
msgid "Edit selected"
msgstr ""
#: notebook/templates/tree.html:42
msgid "Delete selected"
msgstr ""
#: notebook/templates/tree.html:50
msgid "Click to browse for a file to upload."
msgstr ""
#: notebook/templates/tree.html:51
msgid "Upload"
msgstr ""
#: notebook/templates/tree.html:65
msgid "Text File"
msgstr ""
#: notebook/templates/tree.html:68
msgid "Folder"
msgstr ""
#: notebook/templates/tree.html:72
msgid "Terminal"
msgstr ""
#: notebook/templates/tree.html:76
msgid "Terminals Unavailable"
msgstr ""
#: notebook/templates/tree.html:82
msgid "Refresh notebook list"
msgstr ""
#: notebook/templates/tree.html:90
msgid "Select All / None"
msgstr ""
#: notebook/templates/tree.html:93
msgid "Select..."
msgstr ""
#: notebook/templates/tree.html:98
msgid "Select All Folders"
msgstr ""
#: notebook/templates/tree.html:98
msgid "Folders"
msgstr ""
#: notebook/templates/tree.html:99
msgid "Select All Notebooks"
msgstr ""
#: notebook/templates/tree.html:99
msgid "All Notebooks"
msgstr ""
#: notebook/templates/tree.html:100
msgid "Select Running Notebooks"
msgstr ""
#: notebook/templates/tree.html:100
msgid "Running"
msgstr ""
#: notebook/templates/tree.html:101
msgid "Select All Files"
msgstr ""
#: notebook/templates/tree.html:101
msgid "Files"
msgstr ""
#: notebook/templates/tree.html:114
msgid "Last Modified"
msgstr ""
#: notebook/templates/tree.html:120
msgid "Name"
msgstr ""
#: notebook/templates/tree.html:130
msgid "Currently running Jupyter processes"
msgstr ""
#: notebook/templates/tree.html:134
msgid "Refresh running list"
msgstr ""
#: notebook/templates/tree.html:150
msgid "There are no terminals running."
msgstr ""
#: notebook/templates/tree.html:152
msgid "Terminals are unavailable."
msgstr ""
#: notebook/templates/tree.html:162
msgid "Notebooks"
msgstr ""
#: notebook/templates/tree.html:169
msgid "There are no notebooks running."
msgstr ""
#: notebook/templates/tree.html:178
msgid "Clusters tab is now provided by IPython parallel."
msgstr ""
#: notebook/templates/tree.html:179
msgid "See '<a href=\"https://github.com/ipython/ipyparallel\">IPython parallel</a>' for installation details."
msgstr ""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,746 +0,0 @@
# Translations template for Jupyter.
# Copyright (C) 2017 ORGANIZATION
# This file is distributed under the same license as the Jupyter project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Jupyter VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2017-07-07 12:48-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
#: notebook/templates/404.html:3
msgid "You are requesting a page that does not exist!"
msgstr "U vraagt een pagina die niet bestaat!"
#: notebook/templates/edit.html:37
msgid "current mode"
msgstr "huidige modus"
#: notebook/templates/edit.html:48 notebook/templates/notebook.html:78
msgid "File"
msgstr "Bestand"
#: notebook/templates/edit.html:50 notebook/templates/tree.html:57
msgid "New"
msgstr "Nieuw"
#: notebook/templates/edit.html:51
msgid "Save"
msgstr "Opslaan"
#: notebook/templates/edit.html:52 notebook/templates/tree.html:36
msgid "Rename"
msgstr "Hernoemen"
#: notebook/templates/edit.html:53 notebook/templates/tree.html:38
msgid "Download"
msgstr "Downloaden"
#: notebook/templates/edit.html:56 notebook/templates/notebook.html:131
#: notebook/templates/tree.html:41
msgid "Edit"
msgstr "Bewerken"
#: notebook/templates/edit.html:58
msgid "Find"
msgstr "Vinden"
#: notebook/templates/edit.html:59
msgid "Find &amp; Replace"
msgstr "Zoeken en vervangen"
#: notebook/templates/edit.html:61
msgid "Key Map"
msgstr "Sleutelkaart"
#: notebook/templates/edit.html:62
msgid "Default"
msgstr "Standaard"
#: notebook/templates/edit.html:63
msgid "Sublime Text"
msgstr "Sublime Tekst"
#: notebook/templates/edit.html:68 notebook/templates/notebook.html:159
#: notebook/templates/tree.html:40
msgid "View"
msgstr "Bekijken"
#: notebook/templates/edit.html:70 notebook/templates/notebook.html:162
msgid "Show/Hide the logo and notebook title (above menu bar)"
msgstr ""
"Het logo en de titel van het notebook weergeven/verbergen (boven "
"menubalk)"
#: notebook/templates/edit.html:71 notebook/templates/notebook.html:163
msgid "Toggle Header"
msgstr "Koptekst in- of uitschakelen"
#: notebook/templates/edit.html:72 notebook/templates/notebook.html:171
msgid "Toggle Line Numbers"
msgstr "Regelnummers in- of uitschakelen"
#: notebook/templates/edit.html:75
msgid "Language"
msgstr "Taal"
#: notebook/templates/error.html:23
msgid "The error was:"
msgstr "De fout was:"
#: notebook/templates/login.html:24
msgid "Password or token:"
msgstr "Wachtwoord of token:"
#: notebook/templates/login.html:26
msgid "Password:"
msgstr "Wachtwoord:"
#: notebook/templates/login.html:31
msgid "Log in"
msgstr "Aanmelden"
#: notebook/templates/login.html:39
msgid "No login available, you shouldn't be seeing this page."
msgstr "Geen login beschikbaar, u zou deze pagina niet moeten zien."
#: notebook/templates/logout.html:24
#, python-format
msgid "Proceed to the <a href=\"%(base_url)s\">dashboard"
msgstr "Ga naar het dashboard <a href=\"%(base_url)s\">"
#: notebook/templates/logout.html:26
#, python-format
msgid "Proceed to the <a href=\"%(base_url)slogin\">login page"
msgstr "Ga naar de <a href=\"%(base_url)slogin\">login pagina"
#: notebook/templates/notebook.html:62
msgid "Menu"
msgstr "Menu"
#: notebook/templates/notebook.html:65 notebook/templates/notebook.html:254
msgid "Kernel"
msgstr "Kernel"
#: notebook/templates/notebook.html:68
msgid "This notebook is read-only"
msgstr "Dit notebook is alleen-lezen"
#: notebook/templates/notebook.html:81
msgid "New Notebook"
msgstr "Nieuw notebook"
#: notebook/templates/notebook.html:85
msgid "Opens a new window with the Dashboard view"
msgstr "Opent een nieuw venster met de dashboardweergave"
#: notebook/templates/notebook.html:86
msgid "Open..."
msgstr "Open..."
#: notebook/templates/notebook.html:90
msgid "Open a copy of this notebook's contents and start a new kernel"
msgstr ""
"Een kopie van de inhoud van dit notebook openen en een nieuwe kernel "
"starten"
#: notebook/templates/notebook.html:91
msgid "Make a Copy..."
msgstr "Maak een kopie..."
#: notebook/templates/notebook.html:92
msgid "Rename..."
msgstr "Hernoemen..."
#: notebook/templates/notebook.html:93
msgid "Save and Checkpoint"
msgstr "Opslaan en Checkpoint"
#: notebook/templates/notebook.html:96
msgid "Revert to Checkpoint"
msgstr "Terugkeren naar Checkpoint"
#: notebook/templates/notebook.html:106
msgid "Print Preview"
msgstr "Afdrukvoorbeeld"
#: notebook/templates/notebook.html:107
msgid "Download as"
msgstr "Download als"
#: notebook/templates/notebook.html:109
msgid "Notebook (.ipynb)"
msgstr "Notebook (.ipynb)"
#: notebook/templates/notebook.html:110
msgid "Script"
msgstr "Script"
#: notebook/templates/notebook.html:111
msgid "HTML (.html)"
msgstr "HTML (.html)"
#: notebook/templates/notebook.html:112
msgid "Markdown (.md)"
msgstr "Markdown (.md)"
#: notebook/templates/notebook.html:113
msgid "reST (.rst)"
msgstr "reST (.rst)"
#: notebook/templates/notebook.html:114
msgid "LaTeX (.tex)"
msgstr "LaTeX (.tex)"
#: notebook/templates/notebook.html:115
msgid "PDF via LaTeX (.pdf)"
msgstr "PDF via LaTeX (.pdf)"
#: notebook/templates/notebook.html:118
msgid "Deploy as"
msgstr "Implementeren als"
#: notebook/templates/notebook.html:123
msgid "Trust the output of this notebook"
msgstr "Vertrouwen op de uitvoer van dit notebook"
#: notebook/templates/notebook.html:124
msgid "Trust Notebook"
msgstr "Notebook vertrouwen"
#: notebook/templates/notebook.html:127
msgid "Shutdown this notebook's kernel, and close this window"
msgstr "De kernel van deze notebook afsluiten en dit venster sluiten"
#: notebook/templates/notebook.html:128
msgid "Close and Halt"
msgstr "Sluiten en Halt"
#: notebook/templates/notebook.html:133
msgid "Cut Cells"
msgstr "Cellen knippen"
#: notebook/templates/notebook.html:134
msgid "Copy Cells"
msgstr "Cellen kopiëren"
#: notebook/templates/notebook.html:135
msgid "Paste Cells Above"
msgstr "Cellen boven plakken"
#: notebook/templates/notebook.html:136
msgid "Paste Cells Below"
msgstr "Cellen eronder plakken"
#: notebook/templates/notebook.html:137
msgid "Paste Cells &amp; Replace"
msgstr "Cellen plakken en vervangen"
#: notebook/templates/notebook.html:138
msgid "Delete Cells"
msgstr "Cellen verwijderen"
#: notebook/templates/notebook.html:139
msgid "Undo Delete Cells"
msgstr "Verwijdercellen ongedaan maken"
#: notebook/templates/notebook.html:141
msgid "Split Cell"
msgstr "Cel splitsen"
#: notebook/templates/notebook.html:142
msgid "Merge Cell Above"
msgstr "Cel boven samenvoegen"
#: notebook/templates/notebook.html:143
msgid "Merge Cell Below"
msgstr "Cel eronder samenvoegen"
#: notebook/templates/notebook.html:145
msgid "Move Cell Up"
msgstr "Cel omhoog verplaatsen"
#: notebook/templates/notebook.html:146
msgid "Move Cell Down"
msgstr "Cel omlaag verplaatsen"
#: notebook/templates/notebook.html:148
msgid "Edit Notebook Metadata"
msgstr "Metagegevens van notebook bewerken"
#: notebook/templates/notebook.html:150
msgid "Find and Replace"
msgstr "Zoeken en vervangen"
#: notebook/templates/notebook.html:152
msgid "Cut Cell Attachments"
msgstr "Celbijlagen knippen"
#: notebook/templates/notebook.html:153
msgid "Copy Cell Attachments"
msgstr "Celbijlagen kopiëren"
#: notebook/templates/notebook.html:154
msgid "Paste Cell Attachments"
msgstr "Celbijlagen plakken"
#: notebook/templates/notebook.html:156
msgid "Insert Image"
msgstr "Afbeelding invoegen"
#: notebook/templates/notebook.html:166
msgid "Show/Hide the action icons (below menu bar)"
msgstr "De actiepictogrammen weergeven/verbergen (onder menubalk)"
#: notebook/templates/notebook.html:167
msgid "Toggle Toolbar"
msgstr "Werkbalk in- en uitschakelen"
#: notebook/templates/notebook.html:170
msgid "Show/Hide line numbers in cells"
msgstr "Lijnnummers weergeven/verbergen in cellen"
#: notebook/templates/notebook.html:174
msgid "Cell Toolbar"
msgstr "Celwerkbalk"
#: notebook/templates/notebook.html:179
msgid "Insert"
msgstr "Invoegen"
#: notebook/templates/notebook.html:182
msgid "Insert an empty Code cell above the currently active cell"
msgstr "Een lege codecel boven de actieve cel invoegen"
#: notebook/templates/notebook.html:183
msgid "Insert Cell Above"
msgstr "Cel boven invoegen"
#: notebook/templates/notebook.html:185
msgid "Insert an empty Code cell below the currently active cell"
msgstr "Een lege codecel onder de actieve cel invoegen"
#: notebook/templates/notebook.html:186
msgid "Insert Cell Below"
msgstr "Cel eronder invoegen"
#: notebook/templates/notebook.html:189
msgid "Cell"
msgstr "Cel"
#: notebook/templates/notebook.html:191
msgid "Run this cell, and move cursor to the next one"
msgstr "Deze cel uitvoeren en cursor naar de volgende cel verplaatsen"
#: notebook/templates/notebook.html:192
msgid "Run Cells"
msgstr "Cellen uitvoeren"
#: notebook/templates/notebook.html:193
msgid "Run this cell, select below"
msgstr "Voer deze cel uit en selecteer de cel eronder"
#: notebook/templates/notebook.html:194
msgid "Run Cells and Select Below"
msgstr "Cellen uitvoeren en de cel eronder selecteren"
#: notebook/templates/notebook.html:195
msgid "Run this cell, insert below"
msgstr "Voer deze cel uit en voeg een cel toe"
#: notebook/templates/notebook.html:196
msgid "Run Cells and Insert Below"
msgstr "Cellen uitvoeren en voeg een cel toe"
#: notebook/templates/notebook.html:197
msgid "Run all cells in the notebook"
msgstr "Alle cellen in het notebook uitvoeren"
#: notebook/templates/notebook.html:198
msgid "Run All"
msgstr "Alles uitvoeren"
#: notebook/templates/notebook.html:199
msgid "Run all cells above (but not including) this cell"
msgstr "Alle cellen boven (maar niet inclusief) deze cel uitvoeren"
#: notebook/templates/notebook.html:200
msgid "Run All Above"
msgstr "All cellen hierboven uitvoeren"
#: notebook/templates/notebook.html:201
msgid "Run this cell and all cells below it"
msgstr "Voer deze cel en alle cellen eronder uit."
#: notebook/templates/notebook.html:202
msgid "Run All Below"
msgstr "Voer alles eronder uit"
#: notebook/templates/notebook.html:205
msgid ""
"All cells in the notebook have a cell type. By default, new cells are "
"created as 'Code' cells"
msgstr ""
"Alle cellen in het notebook hebben een celtype. Standaard worden nieuwe "
"cellen gemaakt als 'Code'-cellen"
#: notebook/templates/notebook.html:206
msgid "Cell Type"
msgstr "Celtype"
#: notebook/templates/notebook.html:209
msgid ""
"Contents will be sent to the kernel for execution, and output will display "
"in the footer of cell"
msgstr ""
"Inhoud wordt verzonden naar de kernel voor uitvoering, en de uitvoer wordt "
"weergegeven in de voettekst van de cel"
#: notebook/templates/notebook.html:212
msgid "Contents will be rendered as HTML and serve as explanatory text"
msgstr "Inhoud wordt weergegeven als HTML en dient als verklarende tekst"
#: notebook/templates/notebook.html:213 notebook/templates/notebook.html:298
msgid "Markdown"
msgstr "Markdown"
#: notebook/templates/notebook.html:215
msgid "Contents will pass through nbconvert unmodified"
msgstr "De inhoud zal niet worden gewijzigd door nbconvert"
#: notebook/templates/notebook.html:216
msgid "Raw NBConvert"
msgstr "Raw NBConvert"
#: notebook/templates/notebook.html:220
msgid "Current Outputs"
msgstr "Huidige uitvoer"
#: notebook/templates/notebook.html:223
msgid "Hide/Show the output of the current cell"
msgstr "De uitvoer van de huidige cel verbergen/weergeven"
#: notebook/templates/notebook.html:224 notebook/templates/notebook.html:240
msgid "Toggle"
msgstr "In- en uitschakelen"
#: notebook/templates/notebook.html:227
msgid "Scroll the output of the current cell"
msgstr "De uitvoer van de huidige cel scrollen"
#: notebook/templates/notebook.html:228 notebook/templates/notebook.html:244
msgid "Toggle Scrolling"
msgstr "Scrollen in- en uitschakelen"
#: notebook/templates/notebook.html:231
msgid "Clear the output of the current cell"
msgstr "De uitvoer van de huidige cel wissen"
#: notebook/templates/notebook.html:232 notebook/templates/notebook.html:248
msgid "Clear"
msgstr "Wissen"
#: notebook/templates/notebook.html:236
msgid "All Output"
msgstr "Alle uitvoer"
#: notebook/templates/notebook.html:239
msgid "Hide/Show the output of all cells"
msgstr "De uitvoer van alle cellen verbergen/weergeven"
#: notebook/templates/notebook.html:243
msgid "Scroll the output of all cells"
msgstr "Door de uitvoer van alle cellen scrollen"
#: notebook/templates/notebook.html:247
msgid "Clear the output of all cells"
msgstr "De uitvoer van alle cellen wissen"
#: notebook/templates/notebook.html:257
msgid "Send Keyboard Interrupt (CTRL-C) to the Kernel"
msgstr "Toetsenbordinterruptie (Ctrl-C) naar de kernel verzenden"
#: notebook/templates/notebook.html:258
msgid "Interrupt"
msgstr "Onderbreken"
#: notebook/templates/notebook.html:261
msgid "Restart the Kernel"
msgstr "De kernel opnieuw starten"
#: notebook/templates/notebook.html:262
msgid "Restart"
msgstr "Opnieuw starten"
#: notebook/templates/notebook.html:265
msgid "Restart the Kernel and clear all output"
msgstr "Start de kernel opnieuw en schakel alle uitvoer uit"
#: notebook/templates/notebook.html:266
msgid "Restart &amp; Clear Output"
msgstr "Uitvoer opnieuw starten en wissen"
#: notebook/templates/notebook.html:269
msgid "Restart the Kernel and re-run the notebook"
msgstr "Start de kernel opnieuw en voer notebook opnieuw uit"
#: notebook/templates/notebook.html:270
msgid "Restart &amp; Run All"
msgstr "Alles opnieuw starten en uitvoeren"
#: notebook/templates/notebook.html:273
msgid "Reconnect to the Kernel"
msgstr "Opnieuw verbinding maken met de kernel"
#: notebook/templates/notebook.html:274
msgid "Reconnect"
msgstr "Sluit"
#: notebook/templates/notebook.html:282
msgid "Change kernel"
msgstr "Kernel wijzigen"
#: notebook/templates/notebook.html:287
msgid "Help"
msgstr "Help"
#: notebook/templates/notebook.html:290
msgid "A quick tour of the notebook user interface"
msgstr "Een snelle rondleiding door de gebruikersinterface van de notebook"
#: notebook/templates/notebook.html:290
msgid "User Interface Tour"
msgstr "Gebruikersinterfacetour"
#: notebook/templates/notebook.html:291
msgid "Opens a tooltip with all keyboard shortcuts"
msgstr "Hiermee opent u een tooltop met alle sneltoetsen"
#: notebook/templates/notebook.html:291
msgid "Keyboard Shortcuts"
msgstr "Sneltoetsen"
#: notebook/templates/notebook.html:292
msgid "Opens a dialog allowing you to edit Keyboard shortcuts"
msgstr "Hiermee opent u een dialoogvenster waarmee u sneltoetsen bewerken"
#: notebook/templates/notebook.html:292
msgid "Edit Keyboard Shortcuts"
msgstr "Sneltoetsen bewerken"
#: notebook/templates/notebook.html:297
msgid "Notebook Help"
msgstr "Help voor notebooks"
#: notebook/templates/notebook.html:303
msgid "Opens in a new window"
msgstr "Opent in een nieuw venster"
#: notebook/templates/notebook.html:319
msgid "About Jupyter Notebook"
msgstr "Over Jupyter Notebook"
#: notebook/templates/notebook.html:319
msgid "About"
msgstr "Over"
#: notebook/templates/page.html:114
msgid "Jupyter Notebook requires JavaScript."
msgstr "Jupyter Notebook vereist JavaScript."
#: notebook/templates/page.html:115
msgid "Please enable it to proceed. "
msgstr "Schakel het in om door te gaan. "
#: notebook/templates/page.html:121
msgid "dashboard"
msgstr "Dashboard"
#: notebook/templates/page.html:132
msgid "Logout"
msgstr "Logout"
#: notebook/templates/page.html:134
msgid "Login"
msgstr "Login"
#: notebook/templates/tree.html:23
msgid "Files"
msgstr "Bestanden"
#: notebook/templates/tree.html:24
msgid "Running"
msgstr "Actieve processen"
#: notebook/templates/tree.html:25
msgid "Clusters"
msgstr "Clusters"
#: notebook/templates/tree.html:32
msgid "Select items to perform actions on them."
msgstr "Selecteer items om acties op uit te voeren."
#: notebook/templates/tree.html:35
msgid "Duplicate selected"
msgstr "Duplicaat geselecteerd"
#: notebook/templates/tree.html:35
msgid "Duplicate"
msgstr "Dupliceer"
#: notebook/templates/tree.html:36
msgid "Rename selected"
msgstr "Naam wijzigen van geselecteerde"
#: notebook/templates/tree.html:37
msgid "Move selected"
msgstr "Verplaats geselecteerde"
#: notebook/templates/tree.html:37
msgid "Move"
msgstr "Verplaatsen"
#: notebook/templates/tree.html:38
msgid "Download selected"
msgstr "Download geselecteerde"
#: notebook/templates/tree.html:39
msgid "Shutdown selected notebook(s)"
msgstr "Geselecteerde notebook(s) afsluiten"
#: notebook/templates/notebook.html:278 notebook/templates/tree.html:39
msgid "Shutdown"
msgstr "Afsluiten"
#: notebook/templates/tree.html:40
msgid "View selected"
msgstr "Geef geselecteerde weer"
#: notebook/templates/tree.html:41
msgid "Edit selected"
msgstr "Bewerk geselecteerde"
#: notebook/templates/tree.html:42
msgid "Delete selected"
msgstr "Verwijder geselecteerde"
#: notebook/templates/tree.html:50
msgid "Click to browse for a file to upload."
msgstr "Klik hier om te zoeken naar een bestand dat u wilt uploaden."
#: notebook/templates/tree.html:51
msgid "Upload"
msgstr "Uploaden"
#: notebook/templates/tree.html:65
msgid "Text File"
msgstr "Tekstbestand"
#: notebook/templates/tree.html:68
msgid "Folder"
msgstr "Map"
#: notebook/templates/tree.html:72
msgid "Terminal"
msgstr "Terminal"
#: notebook/templates/tree.html:76
msgid "Terminals Unavailable"
msgstr "Terminals Niet Beschikbaar"
#: notebook/templates/tree.html:82
msgid "Refresh notebook list"
msgstr "Notebook-lijst vernieuwen"
#: notebook/templates/tree.html:90
msgid "Select All / None"
msgstr "Selecteer Alles / Geen"
#: notebook/templates/tree.html:93
msgid "Select..."
msgstr "Selecteer..."
#: notebook/templates/tree.html:98
msgid "Select All Folders"
msgstr "Alle Mappen Selecteren"
#: notebook/templates/tree.html:98
msgid "Folders"
msgstr "Mappen"
#: notebook/templates/tree.html:99
msgid "Select All Notebooks"
msgstr "Alle Notebooks Selecteren"
#: notebook/templates/tree.html:99
msgid "All Notebooks"
msgstr "Alle notebooks"
#: notebook/templates/tree.html:100
msgid "Select Running Notebooks"
msgstr "Actieve Notebooks Selecteren"
#: notebook/templates/tree.html:100
msgid "Running"
msgstr "Actieve Processen"
#: notebook/templates/tree.html:101
msgid "Select All Files"
msgstr "Alle Bestanden Selecteren"
#: notebook/templates/tree.html:101
msgid "Files"
msgstr "Bestanden"
#: notebook/templates/tree.html:114
msgid "Last Modified"
msgstr "Laatst gewijzigd"
#: notebook/templates/tree.html:120
msgid "Name"
msgstr "Naam"
#: notebook/templates/tree.html:130
msgid "Currently running Jupyter processes"
msgstr "Momenteel actieve Jupyter processen"
#: notebook/templates/tree.html:134
msgid "Refresh running list"
msgstr "Actieve processen lijst vernieuwen"
#: notebook/templates/tree.html:150
msgid "There are no terminals running."
msgstr "Er zijn geen terminals actief."
#: notebook/templates/tree.html:152
msgid "Terminals are unavailable."
msgstr "Terminals zijn niet beschikbaar."
#: notebook/templates/tree.html:162
msgid "Notebooks"
msgstr "Notebooks"
#: notebook/templates/tree.html:169
msgid "There are no notebooks running."
msgstr "Er worden geen notebooks uitgevoerd."
#: notebook/templates/tree.html:178
msgid "Clusters tab is now provided by IPython parallel."
msgstr "Clusters tabblad wordt nu geleverd door IPython parallel."
#: notebook/templates/tree.html:179
msgid ""
"See '<a href=\"https://github.com/ipython/ipyparallel\">IPython "
"parallel</a>' for installation details."
msgstr ""
"Zie '<a href=\"https://github.com/ipython/ipyparallel\">IPython "
"parallel</a>' voor installatiegegevens."

@ -1,607 +0,0 @@
# Translations template for Jupyter.
# Copyright (C) 2017 ORGANIZATION
# This file is distributed under the same license as the Jupyter project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Jupyter VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2017-07-08 21:52-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
#: notebook/notebookapp.py:53
msgid "The Jupyter Notebook requires tornado >= 4.0"
msgstr "De Jupyter Notebook vereist tornado >= 4.0"
#: notebook/notebookapp.py:57
msgid "The Jupyter Notebook requires tornado >= 4.0, but you have < 1.1.0"
msgstr "De Jupyter Notebook vereist tornado >= 4.0, maar je hebt < 1.1.0"
#: notebook/notebookapp.py:59
#, python-format
msgid "The Jupyter Notebook requires tornado >= 4.0, but you have %s"
msgstr "De Jupyter Notebook vereist tornado >= 4.0, maar je hebt %s"
#: notebook/notebookapp.py:209
msgid "The `ignore_minified_js` flag is deprecated and no longer works."
msgstr "De vlag 'ignore_minified_js' is afgeschaft en werkt niet meer."
#: notebook/notebookapp.py:210
#, python-format
msgid ""
"Alternatively use `%s` when working on the notebook's Javascript and LESS"
msgstr ""
"U ook '%s' gebruiken bij het werken aan de Javascript van de notebook en "
"LESS"
#: notebook/notebookapp.py:211
msgid ""
"The `ignore_minified_js` flag is deprecated and will be removed in Notebook "
"6.0"
msgstr ""
"De vlag 'ignore_minified_js' wordt afgeschaft en wordt verwijderd in "
"Notebook 6.0"
#: notebook/notebookapp.py:389
msgid "List currently running notebook servers."
msgstr "Lijst met momenteel draaiende notebookservers."
#: notebook/notebookapp.py:393
msgid "Produce machine-readable JSON output."
msgstr "Productie computer-leesbare JSON-uitvoer."
#: notebook/notebookapp.py:397
msgid ""
"If True, each line of output will be a JSON object with the details from the"
" server info file."
msgstr ""
"Als dit True is, zal elke uitvoerregel een JSON-object worden met de details uit het "
"serverinfobestand."
#: notebook/notebookapp.py:402
msgid "Currently running servers:"
msgstr "Momenteel draaiende servers:"
#: notebook/notebookapp.py:419
msgid "Don't open the notebook in a browser after startup."
msgstr "Open het notebook niet in een browser na het opstarten."
#: notebook/notebookapp.py:423
msgid ""
"DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib."
msgstr ""
"UITGESCHAKELD: gebruik %pylab of %matplotlib in het notebook om "
"matplotlib in te schakelen."
#: notebook/notebookapp.py:439
msgid "Allow the notebook to be run from root user."
msgstr "Sta toe dat het notebook vanaf de root user kan worden uitgevoerd."
#: notebook/notebookapp.py:470
msgid ""
"The Jupyter HTML Notebook.\n"
" \n"
" This launches a Tornado based HTML Notebook Server that serves up an HTML5/Javascript Notebook client."
msgstr ""
"De Jupyter HTML Notebook.\n"
" \n"
"Hiermee wordt een op Tornado gebaseerde HTML-notebookserver gelanceerd die een HTML5/Javascript-laptopclient bedient."
#: notebook/notebookapp.py:509
msgid ""
"Deprecated: Use minified JS file or not, mainly use during dev to avoid JS "
"recompilation"
msgstr ""
"Afgeschaft: Gebruik minified JS-bestand of niet, voornamelijk gebruiken "
"tijdens dev om JS recompilatie te voorkomen"
#: notebook/notebookapp.py:540
msgid "Set the Access-Control-Allow-Credentials: true header"
msgstr "De access-control-allow-credentials instellen: true header"
#: notebook/notebookapp.py:544
msgid "Whether to allow the user to run the notebook as root."
msgstr "Of de gebruiker het notebook als root mag activeren."
#: notebook/notebookapp.py:548
msgid "The default URL to redirect to from `/`"
msgstr "De standaard-URL om naar '/' te leiden"
#: notebook/notebookapp.py:552
msgid "The IP address the notebook server will listen on."
msgstr "Het IP-adres waar de notebookserver op geactiveerd wordt."
#: notebook/notebookapp.py:565
#, python-format
msgid ""
"Cannot bind to localhost, using 127.0.0.1 as default ip\n"
"%s"
msgstr ""
"Kan niet binden aan localhost, met 127.0.0.1 als standaardip\n"
"%s"
#: notebook/notebookapp.py:579
msgid "The port the notebook server will listen on."
msgstr "De port waarop de notebookserver geactiveerd wordt."
#: notebook/notebookapp.py:583
msgid ""
"The number of additional ports to try if the specified port is not "
"available."
msgstr ""
"Het aantal extra ports dat moet worden geprobeerd als de opgegeven port "
"niet beschikbaar is."
#: notebook/notebookapp.py:587
msgid "The full path to an SSL/TLS certificate file."
msgstr "Het volledige pad naar een SSL/TLS-certificaatbestand."
#: notebook/notebookapp.py:591
msgid "The full path to a private key file for usage with SSL/TLS."
msgstr ""
"Het volledige pad naar een privésleutelbestand voor gebruik met SSL/TLS."
#: notebook/notebookapp.py:595
msgid ""
"The full path to a certificate authority certificate for SSL/TLS client "
"authentication."
msgstr ""
"Het volledige pad naar een certificaat van certificaatautoriteit voor "
"SSL/TLS-clientverificatie."
#: notebook/notebookapp.py:599
msgid "The file where the cookie secret is stored."
msgstr "Het bestand waarin het cookiegeheim wordt opgeslagen."
#: notebook/notebookapp.py:628
#, python-format
msgid "Writing notebook server cookie secret to %s"
msgstr "Cookiegeheim voor notebookserver schrijven naar %s"
#: notebook/notebookapp.py:635
#, python-format
msgid "Could not set permissions on %s"
msgstr "Kan geen machtigingen instellen op %s"
#: notebook/notebookapp.py:640
msgid ""
"Token used for authenticating first-time connections to the server.\n"
"\n"
" When no password is enabled,\n"
" the default is to generate a new, random token.\n"
"\n"
" Setting to an empty string disables authentication altogether, which is NOT RECOMMENDED.\n"
" "
msgstr ""
"Token wordt gebruikt voor het verifiëren van eerste verbindingen met de server.\n"
"\n"
"Wanneer er geen wachtwoord is ingeschakeld,\n"
" de standaardinstelling is het genereren van een nieuwe, willekeurige token.\n"
"\n"
"Als u een lege tekenreeks instelt, wordt de verificatie helemaal uitgeschakeld, wat niet wordt aanbevolen.\n"
" "
#: notebook/notebookapp.py:650
msgid ""
"One-time token used for opening a browser.\n"
" Once used, this token cannot be used again.\n"
" "
msgstr ""
"Eenmalige token die wordt gebruikt voor het openen van een browser.\n"
" Eenmaal gebruikt, kan dit token niet opnieuw worden gebruikt.\n"
" "
#: notebook/notebookapp.py:726
msgid ""
"Specify Where to open the notebook on startup. This is the\n"
" `new` argument passed to the standard library method `webbrowser.open`.\n"
" The behaviour is not guaranteed, but depends on browser support. Valid\n"
" values are:\n"
" 2 opens a new tab,\n"
" 1 opens a new window,\n"
" 0 opens in an existing window.\n"
" See the `webbrowser.open` documentation for details.\n"
" "
msgstr ""
"Geef op waar u het notebook moet openen bij het opstarten. Dit is de\n"
" 'nieuw' argument doorgegeven aan de standaard bibliotheek methode 'webbrowser.open'.\n"
" Het gedrag is niet gegarandeerd, maar is afhankelijk van browserondersteuning. Geldig\n"
" waarden zijn:\n"
" 2 opent een nieuw tabblad,\n"
" 1 opent een nieuw venster,\n"
" 0 wordt geopend in een bestaand venster.\n"
" Zie de documentatie 'webbrowser.open' voor meer informatie.\n"
" "
#: notebook/notebookapp.py:737
msgid "DEPRECATED, use tornado_settings"
msgstr "DEPRECATED, gebruik tornado_settings"
#: notebook/notebookapp.py:742
msgid ""
"\n"
" webapp_settings is deprecated, use tornado_settings.\n"
msgstr ""
"\n"
"webapp_settings is deprecated, gebruik tornado_settings.\n"
#: notebook/notebookapp.py:746
msgid ""
"Supply overrides for the tornado.web.Application that the Jupyter notebook "
"uses."
msgstr ""
"Geef extra instellingen voor de tornado.web.Application die gebruikt wordt door de "
" Jupyter notebook."
#: notebook/notebookapp.py:750
msgid ""
"\n"
" Set the tornado compression options for websocket connections.\n"
"\n"
" This value will be returned from :meth:`WebSocketHandler.get_compression_options`.\n"
" None (default) will disable compression.\n"
" A dict (even an empty one) will enable compression.\n"
"\n"
" See the tornado docs for WebSocketHandler.get_compression_options for details.\n"
" "
msgstr ""
"\n"
"Stel de tornadocompressieopties in voor websocketverbindingen.\n"
"\n"
"Deze waarde wordt geretourneerd van :meth:'WebSocketHandler.get_compression_options'.\n"
" Geen (standaard) schakelt compressie uit.\n"
" Een dict (zelfs een lege) zal compressie mogelijk maken.\n"
"\n"
"Zie de tornadodocumenten voor WebSocketHandler.get_compression_options voor meer informatie.\n"
" "
#: notebook/notebookapp.py:761
msgid "Supply overrides for terminado. Currently only supports \"shell_command\"."
msgstr ""
"Supply overrides voor terminado. Ondersteunt momenteel alleen een "
"\"shell_command\"."
#: notebook/notebookapp.py:764
msgid ""
"Extra keyword arguments to pass to `set_secure_cookie`. See tornado's "
"set_secure_cookie docs for details."
msgstr ""
"Extra trefwoordargumenten om door te geven aan 'set_secure_cookie'. Zie "
"tornado's set_secure_cookie documenten voor meer informatie."
#: notebook/notebookapp.py:768
msgid ""
"Supply SSL options for the tornado HTTPServer.\n"
" See the tornado docs for details."
msgstr ""
"SSL-opties leveren voor de tornado HTTPServer.\n"
" Zie de tornado docs voor meer informatie."
#: notebook/notebookapp.py:772
msgid "Supply extra arguments that will be passed to Jinja environment."
msgstr ""
"Vul extra argumenten aan die zullen worden doorgegeven aan de Jinja environment."
#: notebook/notebookapp.py:776
msgid "Extra variables to supply to jinja templates when rendering."
msgstr "Extra variabelen om aan te vullen aan de jinja-sjablonen bij het renderen."
#: notebook/notebookapp.py:812
msgid "DEPRECATED use base_url"
msgstr "DEPRECATED gebruik base_url"
#: notebook/notebookapp.py:816
msgid "base_project_url is deprecated, use base_url"
msgstr "base_project_url is deprecated, gebruik base_url"
#: notebook/notebookapp.py:832
msgid "Path to search for custom.js, css"
msgstr "Pad om te zoeken naar custom.js, css"
#: notebook/notebookapp.py:844
msgid ""
"Extra paths to search for serving jinja templates.\n"
"\n"
" Can be used to override templates from notebook.templates."
msgstr ""
"Extra paden om te zoeken voor het activeren van jinja-sjablonen.\n"
"\n"
"Kan worden gebruikt om sjablonen van notebook.templates te overschrijven."
#: notebook/notebookapp.py:855
msgid "extra paths to look for Javascript notebook extensions"
msgstr "extra paden om te zoeken naar Javascript-notebookextensies"
#: notebook/notebookapp.py:900
#, python-format
msgid "Using MathJax: %s"
msgstr "MathJax gebruiken: %s"
#: notebook/notebookapp.py:903
msgid "The MathJax.js configuration file that is to be used."
msgstr "Het configuratiebestand MathJax.js dat moet worden gebruikt."
#: notebook/notebookapp.py:908
#, python-format
msgid "Using MathJax configuration file: %s"
msgstr "MathJax-configuratiebestand gebruiken: %s"
#: notebook/notebookapp.py:914
msgid "The notebook manager class to use."
msgstr "De notebook manager klasse te gebruiken."
#: notebook/notebookapp.py:920
msgid "The kernel manager class to use."
msgstr "De kernel manager klasse om te gebruiken."
#: notebook/notebookapp.py:926
msgid "The session manager class to use."
msgstr "De sessie manager klasse die u gebruiken."
#: notebook/notebookapp.py:932
msgid "The config manager class to use"
msgstr "De config manager klasse te gebruiken"
#: notebook/notebookapp.py:953
msgid "The login handler class to use."
msgstr "De login handler klasse te gebruiken."
#: notebook/notebookapp.py:960
msgid "The logout handler class to use."
msgstr "De afmeld handler klasse die u wilt gebruiken."
#: notebook/notebookapp.py:964
msgid ""
"Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-"
"Ip/X-Forwarded-For headerssent by the upstream reverse proxy. Necessary if "
"the proxy handles SSL"
msgstr ""
"X-Scheme/X-Forwarded-Proto en X-Real-Ip/X-Forwarded-For headerssent door de "
"upstream reverse proxy al dan niet vertrouwen. Noodzakelijk als de proxy SSL"
" verwerkt"
#: notebook/notebookapp.py:976
msgid ""
"\n"
" DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib.\n"
" "
msgstr ""
"\n"
"UITGESCHAKELD: gebruik %pylab of %matplotlib in het notebook om matplotlib in te schakelen.\n"
" "
#: notebook/notebookapp.py:988
msgid "Support for specifying --pylab on the command line has been removed."
msgstr ""
"Ondersteuning voor het opgeven van --pylab op de opdrachtregel is "
"verwijderd."
#: notebook/notebookapp.py:990
msgid "Please use `%pylab{0}` or `%matplotlib{0}` in the notebook itself."
msgstr "Gebruik '%pylab{0}' of '%matplotlib{0}' in het notebook zelf."
#: notebook/notebookapp.py:995
msgid "The directory to use for notebooks and kernels."
msgstr "De map die u wilt gebruiken voor notebooks en kernels."
#: notebook/notebookapp.py:1018
#, python-format
msgid "No such notebook dir: '%r'"
msgstr "Geen dergelijke notebook dir: '%r'"
#: notebook/notebookapp.py:1031
msgid "DEPRECATED use the nbserver_extensions dict instead"
msgstr "DEPRECATED gebruikt in plaats daarvan de nbserver_extensions dict"
#: notebook/notebookapp.py:1036
msgid "server_extensions is deprecated, use nbserver_extensions"
msgstr "server_extensions is afgeschaft, gebruik nbserver_extensions"
#: notebook/notebookapp.py:1040
msgid ""
"Dict of Python modules to load as notebook server extensions.Entry values "
"can be used to enable and disable the loading ofthe extensions. The "
"extensions will be loaded in alphabetical order."
msgstr ""
"Dict van Python-modules te laden als notebook server extensies. "
"Invoerwaarden kunnen worden gebruikt om het laden van de extensies in en uit"
" te schakelen. De extensies worden in alfabetische volgorde geladen."
#: notebook/notebookapp.py:1049
msgid "Reraise exceptions encountered loading server extensions?"
msgstr "Exceptions opnieuw weergeven die geraised waren tijdens het laden van"
" de server-extensies?"
#: notebook/notebookapp.py:1052
msgid ""
"(msgs/sec)\n"
" Maximum rate at which messages can be sent on iopub before they are\n"
" limited."
msgstr ""
"(msgs/sec)\n"
" Maximale ratio waarmee berichten op iopub kunnen worden verzonden voordat ze\n"
" worden beperkt."
#: notebook/notebookapp.py:1056
msgid ""
"(bytes/sec)\n"
" Maximum rate at which stream output can be sent on iopub before they are\n"
" limited."
msgstr ""
"(bytes/sec)\n"
" Maximale ratio waarmee streamoutput op iopub kan worden verzonden voordat ze\n"
" worden beperkt."
#: notebook/notebookapp.py:1060
msgid ""
"(sec) Time window used to \n"
" check the message and data rate limits."
msgstr ""
"(sec) Tijdvenster gebruikt om \n"
" de limieten voor het verzenden van berichten en de gegevenssnelheiden te"
" controleren."
#: notebook/notebookapp.py:1071
#, python-format
msgid "No such file or directory: %s"
msgstr "Geen dergelijk bestand of map: %s"
#: notebook/notebookapp.py:1141
msgid "Notebook servers are configured to only be run with a password."
msgstr ""
"Notebookservers zijn geconfigureerd om alleen met een wachtwoord te worden "
"uitgevoerd."
#: notebook/notebookapp.py:1142
msgid "Hint: run the following command to set a password"
msgstr "Tip: voer de volgende opdracht uit om een wachtwoord in te stellen"
#: notebook/notebookapp.py:1143
msgid "\t$ python -m notebook.auth password"
msgstr "\t$ python -m notebook.auth wachtwoord"
#: notebook/notebookapp.py:1181
#, python-format
msgid "The port %i is already in use, trying another port."
msgstr "De port %i is al in gebruik, proberen een andere port."
#: notebook/notebookapp.py:1184
#, python-format
msgid "Permission to listen on port %i denied"
msgstr "Toestemming om te luisteren op port %i geweigerd"
#: notebook/notebookapp.py:1193
msgid ""
"ERROR: the notebook server could not be started because no available port "
"could be found."
msgstr ""
"FOUT: de notebookserver kan niet worden gestart omdat er geen beschikbare "
"port kon worden gevonden."
#: notebook/notebookapp.py:1199
msgid "[all ip addresses on your system]"
msgstr "[alle IP-adressen op uw systeem]"
#: notebook/notebookapp.py:1223
#, python-format
msgid "Terminals not available (error was %s)"
msgstr "Terminals niet beschikbaar (fout was %s)"
#: notebook/notebookapp.py:1259
msgid "interrupted"
msgstr "onderbroken"
#: notebook/notebookapp.py:1261
msgid "y"
msgstr "y"
#: notebook/notebookapp.py:1262
msgid "n"
msgstr "n"
#: notebook/notebookapp.py:1263
#, python-format
msgid "Shutdown this notebook server (%s/[%s])? "
msgstr "Deze notebookserver afsluiten (%s/[%s])? "
#: notebook/notebookapp.py:1269
msgid "Shutdown confirmed"
msgstr "Afsluiten bevestigd"
#: notebook/notebookapp.py:1273
msgid "No answer for 5s:"
msgstr "Geen antwoord voor 5s:"
#: notebook/notebookapp.py:1274
msgid "resuming operation..."
msgstr "hervatting van de werking..."
#: notebook/notebookapp.py:1282
#, python-format
msgid "received signal %s, stopping"
msgstr "ontvangen signaal %s, stoppen"
#: notebook/notebookapp.py:1338
#, python-format
msgid "Error loading server extension %s"
msgstr "Foutladen serverextensie %s"
#: notebook/notebookapp.py:1369
#, python-format
msgid "Shutting down %d kernels"
msgstr "%d-kernels afsluiten"
#: notebook/notebookapp.py:1375
#, python-format
msgid "%d active kernel"
msgid_plural "%d active kernels"
msgstr[0] "%d actieve kernel"
msgstr[1] "%d actieve kernel"
#: notebook/notebookapp.py:1379
#, python-format
msgid ""
"The Jupyter Notebook is running at:\n"
"\r"
"%s"
msgstr ""
"De Jupyter Notebook draait op:\n"
"\r"
"%s"
#: notebook/notebookapp.py:1426
msgid "Running as root is not recommended. Use --allow-root to bypass."
msgstr ""
"Hardlopen als root wordt niet aanbevolen. Gebruik --allow-root te "
"omzeilen."
#: notebook/notebookapp.py:1432
msgid ""
"Use Control-C to stop this server and shut down all kernels (twice to skip "
"confirmation)."
msgstr ""
"Gebruik Control-C om deze server te stoppen en sluit alle kernels af (twee "
"keer om bevestiging over te slaan)."
#: notebook/notebookapp.py:1434
msgid ""
"Welcome to Project Jupyter! Explore the various tools available and their "
"corresponding documentation. If you are interested in contributing to the "
"platform, please visit the communityresources section at "
"http://jupyter.org/community.html."
msgstr ""
"Welkom bij Project Jupyter! Bekijk de verschillende tools die beschikbaar "
"zijn en de bijbehorende documentatie. Als je geïnteresseerd bent om bij te "
"dragen aan het platform, ga dan naar de communityresources sectie op "
"http://jupyter.org/community.html."
#: notebook/notebookapp.py:1445
#, python-format
msgid "No web browser found: %s."
msgstr "Geen webbrowser gevonden: %s."
#: notebook/notebookapp.py:1450
#, python-format
msgid "%s does not exist"
msgstr "%s bestaat niet"
#: notebook/notebookapp.py:1484
msgid "Interrupted..."
msgstr "Onderbroken..."
#: notebook/services/contents/filemanager.py:506
#, python-format
msgid "Serving notebooks from local directory: %s"
msgstr "Notebooks uit lokale map activeren: %s"
#: notebook/services/contents/manager.py:68
msgid "Untitled"
msgstr "Naamloos"

@ -1,480 +0,0 @@
# Translations template for Jupyter.
# Copyright (C) 2017 ORGANIZATION
# This file is distributed under the same license as the Jupyter project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Jupyter VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2017-07-08 21:52-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
#: notebook/notebookapp.py:53
msgid "The Jupyter Notebook requires tornado >= 4.0"
msgstr ""
#: notebook/notebookapp.py:57
msgid "The Jupyter Notebook requires tornado >= 4.0, but you have < 1.1.0"
msgstr ""
#: notebook/notebookapp.py:59
#, python-format
msgid "The Jupyter Notebook requires tornado >= 4.0, but you have %s"
msgstr ""
#: notebook/notebookapp.py:209
msgid "The `ignore_minified_js` flag is deprecated and no longer works."
msgstr ""
#: notebook/notebookapp.py:210
#, python-format
msgid "Alternatively use `%s` when working on the notebook's Javascript and LESS"
msgstr ""
#: notebook/notebookapp.py:211
msgid "The `ignore_minified_js` flag is deprecated and will be removed in Notebook 6.0"
msgstr ""
#: notebook/notebookapp.py:389
msgid "List currently running notebook servers."
msgstr ""
#: notebook/notebookapp.py:393
msgid "Produce machine-readable JSON output."
msgstr ""
#: notebook/notebookapp.py:397
msgid "If True, each line of output will be a JSON object with the details from the server info file."
msgstr ""
#: notebook/notebookapp.py:402
msgid "Currently running servers:"
msgstr ""
#: notebook/notebookapp.py:419
msgid "Don't open the notebook in a browser after startup."
msgstr ""
#: notebook/notebookapp.py:423
msgid "DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib."
msgstr ""
#: notebook/notebookapp.py:439
msgid "Allow the notebook to be run from root user."
msgstr ""
#: notebook/notebookapp.py:470
msgid ""
"The Jupyter HTML Notebook.\n"
" \n"
" This launches a Tornado based HTML Notebook Server that serves up an HTML5/Javascript Notebook client."
msgstr ""
#: notebook/notebookapp.py:509
msgid "Deprecated: Use minified JS file or not, mainly use during dev to avoid JS recompilation"
msgstr ""
#: notebook/notebookapp.py:540
msgid "Set the Access-Control-Allow-Credentials: true header"
msgstr ""
#: notebook/notebookapp.py:544
msgid "Whether to allow the user to run the notebook as root."
msgstr ""
#: notebook/notebookapp.py:548
msgid "The default URL to redirect to from `/`"
msgstr ""
#: notebook/notebookapp.py:552
msgid "The IP address the notebook server will listen on."
msgstr ""
#: notebook/notebookapp.py:565
#, python-format
msgid ""
"Cannot bind to localhost, using 127.0.0.1 as default ip\n"
"%s"
msgstr ""
#: notebook/notebookapp.py:579
msgid "The port the notebook server will listen on."
msgstr ""
#: notebook/notebookapp.py:583
msgid "The number of additional ports to try if the specified port is not available."
msgstr ""
#: notebook/notebookapp.py:587
msgid "The full path to an SSL/TLS certificate file."
msgstr ""
#: notebook/notebookapp.py:591
msgid "The full path to a private key file for usage with SSL/TLS."
msgstr ""
#: notebook/notebookapp.py:595
msgid "The full path to a certificate authority certificate for SSL/TLS client authentication."
msgstr ""
#: notebook/notebookapp.py:599
msgid "The file where the cookie secret is stored."
msgstr ""
#: notebook/notebookapp.py:628
#, python-format
msgid "Writing notebook server cookie secret to %s"
msgstr ""
#: notebook/notebookapp.py:635
#, python-format
msgid "Could not set permissions on %s"
msgstr ""
#: notebook/notebookapp.py:640
msgid ""
"Token used for authenticating first-time connections to the server.\n"
"\n"
" When no password is enabled,\n"
" the default is to generate a new, random token.\n"
"\n"
" Setting to an empty string disables authentication altogether, which is NOT RECOMMENDED.\n"
" "
msgstr ""
#: notebook/notebookapp.py:650
msgid ""
"One-time token used for opening a browser.\n"
" Once used, this token cannot be used again.\n"
" "
msgstr ""
#: notebook/notebookapp.py:726
msgid ""
"Specify Where to open the notebook on startup. This is the\n"
" `new` argument passed to the standard library method `webbrowser.open`.\n"
" The behaviour is not guaranteed, but depends on browser support. Valid\n"
" values are:\n"
" 2 opens a new tab,\n"
" 1 opens a new window,\n"
" 0 opens in an existing window.\n"
" See the `webbrowser.open` documentation for details.\n"
" "
msgstr ""
#: notebook/notebookapp.py:737
msgid "DEPRECATED, use tornado_settings"
msgstr ""
#: notebook/notebookapp.py:742
msgid ""
"\n"
" webapp_settings is deprecated, use tornado_settings.\n"
msgstr ""
#: notebook/notebookapp.py:746
msgid "Supply overrides for the tornado.web.Application that the Jupyter notebook uses."
msgstr ""
#: notebook/notebookapp.py:750
msgid ""
"\n"
" Set the tornado compression options for websocket connections.\n"
"\n"
" This value will be returned from :meth:`WebSocketHandler.get_compression_options`.\n"
" None (default) will disable compression.\n"
" A dict (even an empty one) will enable compression.\n"
"\n"
" See the tornado docs for WebSocketHandler.get_compression_options for details.\n"
" "
msgstr ""
#: notebook/notebookapp.py:761
msgid "Supply overrides for terminado. Currently only supports \"shell_command\"."
msgstr ""
#: notebook/notebookapp.py:764
msgid "Extra keyword arguments to pass to `set_secure_cookie`. See tornado's set_secure_cookie docs for details."
msgstr ""
#: notebook/notebookapp.py:768
msgid ""
"Supply SSL options for the tornado HTTPServer.\n"
" See the tornado docs for details."
msgstr ""
#: notebook/notebookapp.py:772
msgid "Supply extra arguments that will be passed to Jinja environment."
msgstr ""
#: notebook/notebookapp.py:776
msgid "Extra variables to supply to jinja templates when rendering."
msgstr ""
#: notebook/notebookapp.py:812
msgid "DEPRECATED use base_url"
msgstr ""
#: notebook/notebookapp.py:816
msgid "base_project_url is deprecated, use base_url"
msgstr ""
#: notebook/notebookapp.py:832
msgid "Path to search for custom.js, css"
msgstr ""
#: notebook/notebookapp.py:844
msgid ""
"Extra paths to search for serving jinja templates.\n"
"\n"
" Can be used to override templates from notebook.templates."
msgstr ""
#: notebook/notebookapp.py:855
msgid "extra paths to look for Javascript notebook extensions"
msgstr ""
#: notebook/notebookapp.py:900
#, python-format
msgid "Using MathJax: %s"
msgstr ""
#: notebook/notebookapp.py:903
msgid "The MathJax.js configuration file that is to be used."
msgstr ""
#: notebook/notebookapp.py:908
#, python-format
msgid "Using MathJax configuration file: %s"
msgstr ""
#: notebook/notebookapp.py:914
msgid "The notebook manager class to use."
msgstr ""
#: notebook/notebookapp.py:920
msgid "The kernel manager class to use."
msgstr ""
#: notebook/notebookapp.py:926
msgid "The session manager class to use."
msgstr ""
#: notebook/notebookapp.py:932
msgid "The config manager class to use"
msgstr ""
#: notebook/notebookapp.py:953
msgid "The login handler class to use."
msgstr ""
#: notebook/notebookapp.py:960
msgid "The logout handler class to use."
msgstr ""
#: notebook/notebookapp.py:964
msgid "Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headerssent by the upstream reverse proxy. Necessary if the proxy handles SSL"
msgstr ""
#: notebook/notebookapp.py:976
msgid ""
"\n"
" DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib.\n"
" "
msgstr ""
#: notebook/notebookapp.py:988
msgid "Support for specifying --pylab on the command line has been removed."
msgstr ""
#: notebook/notebookapp.py:990
msgid "Please use `%pylab{0}` or `%matplotlib{0}` in the notebook itself."
msgstr ""
#: notebook/notebookapp.py:995
msgid "The directory to use for notebooks and kernels."
msgstr ""
#: notebook/notebookapp.py:1018
#, python-format
msgid "No such notebook dir: '%r'"
msgstr ""
#: notebook/notebookapp.py:1031
msgid "DEPRECATED use the nbserver_extensions dict instead"
msgstr ""
#: notebook/notebookapp.py:1036
msgid "server_extensions is deprecated, use nbserver_extensions"
msgstr ""
#: notebook/notebookapp.py:1040
msgid "Dict of Python modules to load as notebook server extensions.Entry values can be used to enable and disable the loading ofthe extensions. The extensions will be loaded in alphabetical order."
msgstr ""
#: notebook/notebookapp.py:1049
msgid "Reraise exceptions encountered loading server extensions?"
msgstr ""
#: notebook/notebookapp.py:1052
msgid ""
"(msgs/sec)\n"
" Maximum rate at which messages can be sent on iopub before they are\n"
" limited."
msgstr ""
#: notebook/notebookapp.py:1056
msgid ""
"(bytes/sec)\n"
" Maximum rate at which stream output can be sent on iopub before they are\n"
" limited."
msgstr ""
#: notebook/notebookapp.py:1060
msgid ""
"(sec) Time window used to \n"
" check the message and data rate limits."
msgstr ""
#: notebook/notebookapp.py:1071
#, python-format
msgid "No such file or directory: %s"
msgstr ""
#: notebook/notebookapp.py:1141
msgid "Notebook servers are configured to only be run with a password."
msgstr ""
#: notebook/notebookapp.py:1142
msgid "Hint: run the following command to set a password"
msgstr ""
#: notebook/notebookapp.py:1143
msgid "\t$ python -m notebook.auth password"
msgstr ""
#: notebook/notebookapp.py:1181
#, python-format
msgid "The port %i is already in use, trying another port."
msgstr ""
#: notebook/notebookapp.py:1184
#, python-format
msgid "Permission to listen on port %i denied"
msgstr ""
#: notebook/notebookapp.py:1193
msgid "ERROR: the notebook server could not be started because no available port could be found."
msgstr ""
#: notebook/notebookapp.py:1199
msgid "[all ip addresses on your system]"
msgstr ""
#: notebook/notebookapp.py:1223
#, python-format
msgid "Terminals not available (error was %s)"
msgstr ""
#: notebook/notebookapp.py:1259
msgid "interrupted"
msgstr ""
#: notebook/notebookapp.py:1261
msgid "y"
msgstr ""
#: notebook/notebookapp.py:1262
msgid "n"
msgstr ""
#: notebook/notebookapp.py:1263
#, python-format
msgid "Shutdown this notebook server (%s/[%s])? "
msgstr ""
#: notebook/notebookapp.py:1269
msgid "Shutdown confirmed"
msgstr ""
#: notebook/notebookapp.py:1273
msgid "No answer for 5s:"
msgstr ""
#: notebook/notebookapp.py:1274
msgid "resuming operation..."
msgstr ""
#: notebook/notebookapp.py:1282
#, python-format
msgid "received signal %s, stopping"
msgstr ""
#: notebook/notebookapp.py:1338
#, python-format
msgid "Error loading server extension %s"
msgstr ""
#: notebook/notebookapp.py:1369
#, python-format
msgid "Shutting down %d kernels"
msgstr ""
#: notebook/notebookapp.py:1375
#, python-format
msgid "%d active kernel"
msgid_plural "%d active kernels"
msgstr[0] ""
msgstr[1] ""
#: notebook/notebookapp.py:1379
#, python-format
msgid ""
"The Jupyter Notebook is running at:\n"
"\r"
"%s"
msgstr ""
#: notebook/notebookapp.py:1426
msgid "Running as root is not recommended. Use --allow-root to bypass."
msgstr ""
#: notebook/notebookapp.py:1432
msgid "Use Control-C to stop this server and shut down all kernels (twice to skip confirmation)."
msgstr ""
#: notebook/notebookapp.py:1434
msgid "Welcome to Project Jupyter! Explore the various tools available and their corresponding documentation. If you are interested in contributing to the platform, please visit the communityresources section at http://jupyter.org/community.html."
msgstr ""
#: notebook/notebookapp.py:1445
#, python-format
msgid "No web browser found: %s."
msgstr ""
#: notebook/notebookapp.py:1450
#, python-format
msgid "%s does not exist"
msgstr ""
#: notebook/notebookapp.py:1484
msgid "Interrupted..."
msgstr ""
#: notebook/services/contents/filemanager.py:506
#, python-format
msgid "Serving notebooks from local directory: %s"
msgstr ""
#: notebook/services/contents/manager.py:68
msgid "Untitled"
msgstr ""

File diff suppressed because it is too large Load Diff

@ -1,739 +0,0 @@
# Translations template for Jupyter.
# Copyright (C) 2017 ORGANIZATION
# This file is distributed under the same license as the Jupyter project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
#
msgid ""
msgstr ""
"Project-Id-Version: Jupyter VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2017-07-07 12:48-0500\n"
"PO-Revision-Date: 2020-07-06 11:05+0500\n"
"Language-Team: TranslAster <https://github.com/translaster>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
"X-Generator: Poedit 2.3.1\n"
"Last-Translator: Dmitriy Q <dmitry@atsip.ru>\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n"
"Language: ru_RU\n"
#: notebook/templates/404.html:3
msgid "You are requesting a page that does not exist!"
msgstr "Вы запрашиваете страницу, которая не существует!"
#: notebook/templates/edit.html:37
msgid "current mode"
msgstr "текущий режим"
#: notebook/templates/edit.html:48 notebook/templates/notebook.html:78
msgid "File"
msgstr "Файл"
#: notebook/templates/edit.html:50 notebook/templates/tree.html:57
msgid "New"
msgstr "Новый"
#: notebook/templates/edit.html:51
msgid "Save"
msgstr "Сохранить"
#: notebook/templates/edit.html:52 notebook/templates/tree.html:36
msgid "Rename"
msgstr "Переименовать"
#: notebook/templates/edit.html:53 notebook/templates/tree.html:38
msgid "Download"
msgstr "Загрузить"
#: notebook/templates/edit.html:56 notebook/templates/notebook.html:131
#: notebook/templates/tree.html:41
msgid "Edit"
msgstr "Редактировать"
#: notebook/templates/edit.html:58
msgid "Find"
msgstr "Поиск"
#: notebook/templates/edit.html:59
msgid "Find &amp; Replace"
msgstr "Поиск &amp; Замена"
#: notebook/templates/edit.html:61
msgid "Key Map"
msgstr "Сопоставления клавиш"
#: notebook/templates/edit.html:62
msgid "Default"
msgstr "По-умолчанию"
#: notebook/templates/edit.html:63
msgid "Sublime Text"
msgstr "Sublime Text"
#: notebook/templates/edit.html:68 notebook/templates/notebook.html:159
#: notebook/templates/tree.html:40
msgid "View"
msgstr "Вид"
#: notebook/templates/edit.html:70 notebook/templates/notebook.html:162
msgid "Show/Hide the logo and notebook title (above menu bar)"
msgstr "Показать/cкрыть логотип и название блокнота (над строкой меню)"
#: notebook/templates/edit.html:71 notebook/templates/notebook.html:163
msgid "Toggle Header"
msgstr "Показать/скрыть заголовок"
#: notebook/templates/edit.html:72 notebook/templates/notebook.html:171
msgid "Toggle Line Numbers"
msgstr "Показать/скрыть номера строк"
#: notebook/templates/edit.html:75
msgid "Language"
msgstr "Язык"
#: notebook/templates/error.html:23
msgid "The error was:"
msgstr "Ошибка в:"
#: notebook/templates/login.html:24
msgid "Password or token:"
msgstr "Пароль или токен:"
#: notebook/templates/login.html:26
msgid "Password:"
msgstr "Пароль:"
#: notebook/templates/login.html:31
msgid "Log in"
msgstr "Вход"
#: notebook/templates/login.html:39
msgid "No login available, you shouldn't be seeing this page."
msgstr "Без входа недоступно, вы не должны видеть эту страницу."
#: notebook/templates/logout.html:24
#, python-format
msgid "Proceed to the <a href=\"%(base_url)s\">dashboard"
msgstr "Перейдите к <a href=\"%(base_url)s\">панели мониторинга"
#: notebook/templates/logout.html:26
#, python-format
msgid "Proceed to the <a href=\"%(base_url)slogin\">login page"
msgstr "Перейдите к <a href=\"%(base_url)slogin\">странице входа"
#: notebook/templates/notebook.html:62
msgid "Menu"
msgstr "Меню"
#: notebook/templates/notebook.html:65 notebook/templates/notebook.html:254
msgid "Kernel"
msgstr "Ядро"
#: notebook/templates/notebook.html:68
msgid "This notebook is read-only"
msgstr "Этот блокнот только для чтения"
#: notebook/templates/notebook.html:81
msgid "New Notebook"
msgstr "Новый блокнот"
#: notebook/templates/notebook.html:85
msgid "Opens a new window with the Dashboard view"
msgstr "Открывает новое окно с видом панели мониторинга"
#: notebook/templates/notebook.html:86
msgid "Open..."
msgstr "Открыть..."
#: notebook/templates/notebook.html:90
msgid "Open a copy of this notebook's contents and start a new kernel"
msgstr "Откройте копию содержимого этого блокнота и запустите новое ядро"
#: notebook/templates/notebook.html:91
msgid "Make a Copy..."
msgstr "Сделать копию..."
#: notebook/templates/notebook.html:92
msgid "Rename..."
msgstr "Переименовать..."
#: notebook/templates/notebook.html:93
msgid "Save and Checkpoint"
msgstr "Сохранение и контрольная точка"
#: notebook/templates/notebook.html:96
msgid "Revert to Checkpoint"
msgstr "Вернуться к контрольной точке"
#: notebook/templates/notebook.html:106
msgid "Print Preview"
msgstr "Предпросмотр печати"
#: notebook/templates/notebook.html:107
msgid "Download as"
msgstr "Скачать как"
#: notebook/templates/notebook.html:109
msgid "Notebook (.ipynb)"
msgstr "Notebook (.ipynb)"
#: notebook/templates/notebook.html:110
msgid "Script"
msgstr "Скрипт"
#: notebook/templates/notebook.html:111
msgid "HTML (.html)"
msgstr "HTML (.html)"
#: notebook/templates/notebook.html:112
msgid "Markdown (.md)"
msgstr "Markdown (.md)"
#: notebook/templates/notebook.html:113
msgid "reST (.rst)"
msgstr "reST (.rst)"
#: notebook/templates/notebook.html:114
msgid "LaTeX (.tex)"
msgstr "LaTeX (.tex)"
#: notebook/templates/notebook.html:115
msgid "PDF via LaTeX (.pdf)"
msgstr "PDF через LaTeX (.pdf)"
#: notebook/templates/notebook.html:118
msgid "Deploy as"
msgstr "Использовать как"
#: notebook/templates/notebook.html:123
msgid "Trust the output of this notebook"
msgstr "Доверять выводу этого блокнота"
#: notebook/templates/notebook.html:124
msgid "Trust Notebook"
msgstr "Доверять блокноту"
#: notebook/templates/notebook.html:127
msgid "Shutdown this notebook's kernel, and close this window"
msgstr "Выключить ядро этого блокнота и закрыть это окно"
#: notebook/templates/notebook.html:128
msgid "Close and Halt"
msgstr "Закрыть и остановить"
#: notebook/templates/notebook.html:133
msgid "Cut Cells"
msgstr "Вырезать ячейки"
#: notebook/templates/notebook.html:134
msgid "Copy Cells"
msgstr "Копировать ячейки"
#: notebook/templates/notebook.html:135
msgid "Paste Cells Above"
msgstr "Вставить ячейки выше"
#: notebook/templates/notebook.html:136
msgid "Paste Cells Below"
msgstr "Вставить ячейки ниже"
#: notebook/templates/notebook.html:137
msgid "Paste Cells &amp; Replace"
msgstr "Вставить ячейки &amp; заменить"
#: notebook/templates/notebook.html:138
msgid "Delete Cells"
msgstr "Удалить ячейки"
#: notebook/templates/notebook.html:139
msgid "Undo Delete Cells"
msgstr "Отменить удаление ячеек"
#: notebook/templates/notebook.html:141
msgid "Split Cell"
msgstr "Разбить ячейку"
#: notebook/templates/notebook.html:142
msgid "Merge Cell Above"
msgstr "Объединить с ячейкой выше"
#: notebook/templates/notebook.html:143
msgid "Merge Cell Below"
msgstr "Объединить с ячейкой ниже"
#: notebook/templates/notebook.html:145
msgid "Move Cell Up"
msgstr "Переместить ячейку выше"
#: notebook/templates/notebook.html:146
msgid "Move Cell Down"
msgstr "Переместить ячейку ниже"
#: notebook/templates/notebook.html:148
msgid "Edit Notebook Metadata"
msgstr "Редактировать метаданные блокнота"
#: notebook/templates/notebook.html:150
msgid "Find and Replace"
msgstr "Поиск и замена"
#: notebook/templates/notebook.html:152
msgid "Cut Cell Attachments"
msgstr "Вырезать вложения ячейки"
#: notebook/templates/notebook.html:153
msgid "Copy Cell Attachments"
msgstr "Копировать вложения ячейки"
#: notebook/templates/notebook.html:154
msgid "Paste Cell Attachments"
msgstr "Вставить вложения ячейки"
#: notebook/templates/notebook.html:156
msgid "Insert Image"
msgstr "Вставить изображение"
#: notebook/templates/notebook.html:166
msgid "Show/Hide the action icons (below menu bar)"
msgstr "Показать/скрыть значки действий (ниже строки меню)"
#: notebook/templates/notebook.html:167
msgid "Toggle Toolbar"
msgstr "Показать/скрыть панель мониторинга"
#: notebook/templates/notebook.html:170
msgid "Show/Hide line numbers in cells"
msgstr "Показать/скрыть номера строк в ячейках"
#: notebook/templates/notebook.html:174
msgid "Cell Toolbar"
msgstr "Панель инструментов ячейки"
#: notebook/templates/notebook.html:179
msgid "Insert"
msgstr "Вставка"
#: notebook/templates/notebook.html:182
msgid "Insert an empty Code cell above the currently active cell"
msgstr "Вставьте пустую ячейку кода над текущей активной ячейкой"
#: notebook/templates/notebook.html:183
msgid "Insert Cell Above"
msgstr "Вставить ячейку выше"
#: notebook/templates/notebook.html:185
msgid "Insert an empty Code cell below the currently active cell"
msgstr "Вставьте пустую ячейку кода под текущей активной ячейкой"
#: notebook/templates/notebook.html:186
msgid "Insert Cell Below"
msgstr "Вставить ячейку ниже"
#: notebook/templates/notebook.html:189
msgid "Cell"
msgstr "Ячейка"
#: notebook/templates/notebook.html:191
msgid "Run this cell, and move cursor to the next one"
msgstr "Запустить эту ячейку и переместить курсор на следующую"
#: notebook/templates/notebook.html:192
msgid "Run Cells"
msgstr "Запустить ячейки"
#: notebook/templates/notebook.html:193
msgid "Run this cell, select below"
msgstr "Запустить эту ячейку, выбрать ниже"
#: notebook/templates/notebook.html:194
msgid "Run Cells and Select Below"
msgstr "Запустить ячейки и выбрать ниже"
#: notebook/templates/notebook.html:195
msgid "Run this cell, insert below"
msgstr "Запустить эту ячейку, вставить ниже"
#: notebook/templates/notebook.html:196
msgid "Run Cells and Insert Below"
msgstr "Запустить ячейки и вставить ниже"
#: notebook/templates/notebook.html:197
msgid "Run all cells in the notebook"
msgstr "Запустить все ячейки в этом блокноте"
#: notebook/templates/notebook.html:198
msgid "Run All"
msgstr "Запустить всё"
#: notebook/templates/notebook.html:199
msgid "Run all cells above (but not including) this cell"
msgstr "Запустите все ячейки выше (но не включая) этой ячейки"
#: notebook/templates/notebook.html:200
msgid "Run All Above"
msgstr "Запустить всё выше"
#: notebook/templates/notebook.html:201
msgid "Run this cell and all cells below it"
msgstr "Запустить эту ячейку и все ячейки под ней"
#: notebook/templates/notebook.html:202
msgid "Run All Below"
msgstr "Запустить всё ниже"
#: notebook/templates/notebook.html:205
msgid ""
"All cells in the notebook have a cell type. By default, new cells are "
"created as 'Code' cells"
msgstr ""
"Все ячейки в блокноте имеют определенный тип ячеек. По умолчанию новые "
"ячейки создаются как ячейки \"Код\""
#: notebook/templates/notebook.html:206
msgid "Cell Type"
msgstr "Тип ячейки"
#: notebook/templates/notebook.html:209
msgid ""
"Contents will be sent to the kernel for execution, and output will display "
"in the footer of cell"
msgstr ""
"Содержимое будет отправлено ядру для выполнения, а выходные данные будут "
"отображаться в нижнем колонтитуле ячейки"
#: notebook/templates/notebook.html:212
msgid "Contents will be rendered as HTML and serve as explanatory text"
msgstr ""
"Содержимое будет отображаться в формате HTML и служить пояснительным "
"текстом"
#: notebook/templates/notebook.html:213 notebook/templates/notebook.html:298
msgid "Markdown"
msgstr "Markdown"
#: notebook/templates/notebook.html:215
msgid "Contents will pass through nbconvert unmodified"
msgstr "Содержимое будет проходить через nbconvert неизмененным"
#: notebook/templates/notebook.html:216
msgid "Raw NBConvert"
msgstr "Необработанный NBConvert"
#: notebook/templates/notebook.html:220
msgid "Current Outputs"
msgstr "Текущий вывод"
#: notebook/templates/notebook.html:223
msgid "Hide/Show the output of the current cell"
msgstr "Показать/скрыть вывод текущей ячейки"
#: notebook/templates/notebook.html:224 notebook/templates/notebook.html:240
msgid "Toggle"
msgstr "Триггер"
#: notebook/templates/notebook.html:227
msgid "Scroll the output of the current cell"
msgstr "Прокрутить вывод текущей ячейки"
#: notebook/templates/notebook.html:228 notebook/templates/notebook.html:244
msgid "Toggle Scrolling"
msgstr "Триггер скроллинга"
#: notebook/templates/notebook.html:231
msgid "Clear the output of the current cell"
msgstr "Очистите выходные данные текущей ячейки"
#: notebook/templates/notebook.html:232 notebook/templates/notebook.html:248
msgid "Clear"
msgstr "Очистка"
#: notebook/templates/notebook.html:236
msgid "All Output"
msgstr "Весь вывод"
#: notebook/templates/notebook.html:239
msgid "Hide/Show the output of all cells"
msgstr "Скрыть/показать вывод всех ячеек"
#: notebook/templates/notebook.html:243
msgid "Scroll the output of all cells"
msgstr "Прокрутите вывод всех ячеек"
#: notebook/templates/notebook.html:247
msgid "Clear the output of all cells"
msgstr "Очистите выходные данные всех ячеек"
#: notebook/templates/notebook.html:257
msgid "Send Keyboard Interrupt (CTRL-C) to the Kernel"
msgstr "Отправить прерывание клавиатуры (CTRL-C) ядру"
#: notebook/templates/notebook.html:258
msgid "Interrupt"
msgstr "Прервать"
#: notebook/templates/notebook.html:261
msgid "Restart the Kernel"
msgstr "Перезапустить ядро"
#: notebook/templates/notebook.html:262
msgid "Restart"
msgstr "Перезапуск"
#: notebook/templates/notebook.html:265
msgid "Restart the Kernel and clear all output"
msgstr "Перезапустить ядро и очистить все выходные данные"
#: notebook/templates/notebook.html:266
msgid "Restart &amp; Clear Output"
msgstr "Перезапустить &amp; очистить вывод"
#: notebook/templates/notebook.html:269
msgid "Restart the Kernel and re-run the notebook"
msgstr "Перезапустить ядро и перезапустить блокнот"
#: notebook/templates/notebook.html:270
msgid "Restart &amp; Run All"
msgstr "Перезапустить &amp; Запустить всё"
#: notebook/templates/notebook.html:273
msgid "Reconnect to the Kernel"
msgstr "Переподключение к ядру"
#: notebook/templates/notebook.html:274
msgid "Reconnect"
msgstr "Переподключение"
#: notebook/templates/notebook.html:282
msgid "Change kernel"
msgstr "Изменение ядра"
#: notebook/templates/notebook.html:287
msgid "Help"
msgstr "Справка"
#: notebook/templates/notebook.html:290
msgid "A quick tour of the notebook user interface"
msgstr "Краткий обзор пользовательского интерфейса блокнота"
#: notebook/templates/notebook.html:290
msgid "User Interface Tour"
msgstr "Обзор пользовательского интерфейса"
#: notebook/templates/notebook.html:291
msgid "Opens a tooltip with all keyboard shortcuts"
msgstr "Открывает инструмент со всеми сочетаниями клавиш"
#: notebook/templates/notebook.html:291
msgid "Keyboard Shortcuts"
msgstr "Сочетания клавиш"
#: notebook/templates/notebook.html:292
msgid "Opens a dialog allowing you to edit Keyboard shortcuts"
msgstr "Открывает диалог, позволяющий редактировать сочетания клавиш"
#: notebook/templates/notebook.html:292
msgid "Edit Keyboard Shortcuts"
msgstr "Редактировать сочетания клавиш"
#: notebook/templates/notebook.html:297
msgid "Notebook Help"
msgstr "Справка по блокноту"
#: notebook/templates/notebook.html:303
msgid "Opens in a new window"
msgstr "Открыть в новом окне"
#: notebook/templates/notebook.html:319
msgid "About Jupyter Notebook"
msgstr "О блокноте Jupyter"
#: notebook/templates/notebook.html:319
msgid "About"
msgstr "О программе"
#: notebook/templates/page.html:114
msgid "Jupyter Notebook requires JavaScript."
msgstr "Блокнот Jupyter требует JavaScript."
#: notebook/templates/page.html:115
msgid "Please enable it to proceed. "
msgstr "Пожалуйста, позвольте продолжить работу. "
#: notebook/templates/page.html:121
msgid "dashboard"
msgstr "панель мониторинга"
#: notebook/templates/page.html:132
msgid "Logout"
msgstr "Разлогиниться"
#: notebook/templates/page.html:134
msgid "Login"
msgstr "Вход"
#: notebook/templates/tree.html:23 notebook/templates/tree.html:101
msgid "Files"
msgstr "Файлы"
#: notebook/templates/tree.html:24 notebook/templates/tree.html:100
msgid "Running"
msgstr "Запустить"
#: notebook/templates/tree.html:25
msgid "Clusters"
msgstr "Кластеры"
#: notebook/templates/tree.html:32
msgid "Select items to perform actions on them."
msgstr "Выберите элементы для выполнения действий над ними."
#: notebook/templates/tree.html:35
msgid "Duplicate selected"
msgstr "Копировать выбранное"
#: notebook/templates/tree.html:35
msgid "Duplicate"
msgstr "Скопировать"
#: notebook/templates/tree.html:36
msgid "Rename selected"
msgstr "Переименовать выделенное"
#: notebook/templates/tree.html:37
msgid "Move selected"
msgstr "Переместить выделенное"
#: notebook/templates/tree.html:37
msgid "Move"
msgstr "Переместить"
#: notebook/templates/tree.html:38
msgid "Download selected"
msgstr "Скачать выбранное"
#: notebook/templates/tree.html:39
msgid "Shutdown selected notebook(s)"
msgstr "Выключить выбранный(-е) блокнот(ы)"
#: notebook/templates/notebook.html:278 notebook/templates/tree.html:39
msgid "Shutdown"
msgstr "Выключение"
#: notebook/templates/tree.html:40
msgid "View selected"
msgstr "Просмотр выбранного"
#: notebook/templates/tree.html:41
msgid "Edit selected"
msgstr "Редактировать выделенное"
#: notebook/templates/tree.html:42
msgid "Delete selected"
msgstr "Удалить выделенное"
#: notebook/templates/tree.html:50
msgid "Click to browse for a file to upload."
msgstr "Нажмите, чтобы найти файл для загрузки."
#: notebook/templates/tree.html:51
msgid "Upload"
msgstr "Загрузить"
#: notebook/templates/tree.html:65
msgid "Text File"
msgstr "Текстовый файл"
#: notebook/templates/tree.html:68
msgid "Folder"
msgstr "Папка"
#: notebook/templates/tree.html:72
msgid "Terminal"
msgstr "Терминал"
#: notebook/templates/tree.html:76
msgid "Terminals Unavailable"
msgstr "Терминалы недоступны"
#: notebook/templates/tree.html:82
msgid "Refresh notebook list"
msgstr "Обновить список блокнотов"
#: notebook/templates/tree.html:90
msgid "Select All / None"
msgstr "Выбрать всё / ничего"
#: notebook/templates/tree.html:93
msgid "Select..."
msgstr "Выбрать..."
#: notebook/templates/tree.html:98
msgid "Select All Folders"
msgstr "Выбрать все папки"
#: notebook/templates/tree.html:98
msgid "Folders"
msgstr "Папки"
#: notebook/templates/tree.html:99
msgid "Select All Notebooks"
msgstr "Выбрать все блокноты"
#: notebook/templates/tree.html:99
msgid "All Notebooks"
msgstr "Все блокноты"
#: notebook/templates/tree.html:100
msgid "Select Running Notebooks"
msgstr "Запустить выбранные блокноты"
#: notebook/templates/tree.html:101
msgid "Select All Files"
msgstr "Выбрать все файлы"
#: notebook/templates/tree.html:114
msgid "Last Modified"
msgstr "Последнее изменение"
#: notebook/templates/tree.html:120
msgid "Name"
msgstr "Название"
#: notebook/templates/tree.html:130
msgid "Currently running Jupyter processes"
msgstr "Текущие запущенные процессы Jupyter"
#: notebook/templates/tree.html:134
msgid "Refresh running list"
msgstr "Обновить список запущенных"
#: notebook/templates/tree.html:150
msgid "There are no terminals running."
msgstr "Эти терминалы не запущены."
#: notebook/templates/tree.html:152
msgid "Terminals are unavailable."
msgstr "Терминалы недоступны."
#: notebook/templates/tree.html:162
msgid "Notebooks"
msgstr "Блокноты"
#: notebook/templates/tree.html:169
msgid "There are no notebooks running."
msgstr "Эти блокноты не запущены."
#: notebook/templates/tree.html:178
msgid "Clusters tab is now provided by IPython parallel."
msgstr "Вкладка кластеров обеспечивается оболочкой IPython параллельно."
#: notebook/templates/tree.html:179
msgid ""
"See '<a href=\"https://github.com/ipython/ipyparallel\">IPython parallel</"
"a>' for installation details."
msgstr ""
"Смотрите '<a href=\"https://github.com/ipython/ipyparallel\">IPython "
"parallel</a>' для подробной информации об установке."

@ -1,621 +0,0 @@
# Translations template for Jupyter.
# Copyright (C) 2017 ORGANIZATION
# This file is distributed under the same license as the Jupyter project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
#
msgid ""
msgstr ""
"Project-Id-Version: Jupyter VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2017-07-08 21:52-0500\n"
"PO-Revision-Date: 2020-07-06 17:38+0500\n"
"Language-Team: TranslAster <https://github.com/translaster>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
"X-Generator: Poedit 2.3.1\n"
"Last-Translator: Dmitriy Q <dmitry@atsip.ru>\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n"
"Language: ru_RU\n"
#: notebook/notebookapp.py:53
msgid "The Jupyter Notebook requires tornado >= 4.0"
msgstr "Для Jupyter Notebook требуется tornado >= 4.0"
#: notebook/notebookapp.py:57
msgid "The Jupyter Notebook requires tornado >= 4.0, but you have < 1.1.0"
msgstr "Для Jupyter Notebook требуется tornado >= 4.0, но у вас < 1.1.0"
#: notebook/notebookapp.py:59
#, python-format
msgid "The Jupyter Notebook requires tornado >= 4.0, but you have %s"
msgstr "Для Jupyter Notebook требуется tornado >= 4.0, но у вас %s"
#: notebook/notebookapp.py:209
msgid "The `ignore_minified_js` flag is deprecated and no longer works."
msgstr "Флаг `ignore_minified_js` устарел и больше не работает."
#: notebook/notebookapp.py:210
#, python-format
msgid ""
"Alternatively use `%s` when working on the notebook's Javascript and LESS"
msgstr ""
"В качестве альтернативы используйте `%s` при работе с блокнотами Javascript "
"и LESS"
#: notebook/notebookapp.py:211
msgid ""
"The `ignore_minified_js` flag is deprecated and will be removed in Notebook "
"6.0"
msgstr "Флаг `ignore_minified_js` устарел и будет удален в Notebook 6.0"
#: notebook/notebookapp.py:389
msgid "List currently running notebook servers."
msgstr "Список запущенных в данный момент серверов блокнотов."
#: notebook/notebookapp.py:393
msgid "Produce machine-readable JSON output."
msgstr "Произведите машиночитаемый вывод JSON."
#: notebook/notebookapp.py:397
msgid ""
"If True, each line of output will be a JSON object with the details from the "
"server info file."
msgstr ""
"Если True, то каждая строка вывода будет представлять собой объект JSON с "
"подробностями из файла информации сервера."
#: notebook/notebookapp.py:402
msgid "Currently running servers:"
msgstr "В настоящее время работают сервера:"
#: notebook/notebookapp.py:419
msgid "Don't open the notebook in a browser after startup."
msgstr "Не открывайте блокнот в браузере после запуска."
#: notebook/notebookapp.py:423
msgid ""
"DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib."
msgstr ""
"ОТКЛЮЧЕНО: используйте %pylab или %matplotlib в блокноте чтобы включить "
"matplotlib."
#: notebook/notebookapp.py:439
msgid "Allow the notebook to be run from root user."
msgstr "Разрешить запускать блокнот от пользователя root."
#: notebook/notebookapp.py:470
msgid ""
"The Jupyter HTML Notebook.\n"
" \n"
" This launches a Tornado based HTML Notebook Server that serves up an "
"HTML5/Javascript Notebook client."
msgstr ""
"The Jupyter HTML Notebook.\n"
" \n"
" Запускает сервер HTML Notebook на базе Tornado, обслуживающий клиент "
"HTML5/Javascript Notebook."
#: notebook/notebookapp.py:509
msgid ""
"Deprecated: Use minified JS file or not, mainly use during dev to avoid JS "
"recompilation"
msgstr ""
"Устарело: используйте уменьшенный файл JS или нет, в основном используется "
"во время разработки чтобы избежать перекомпиляции JS"
#: notebook/notebookapp.py:540
msgid "Set the Access-Control-Allow-Credentials: true header"
msgstr "Установка контроль-доступа-разрешенные-учетныеанные: заголовок true"
#: notebook/notebookapp.py:544
msgid "Whether to allow the user to run the notebook as root."
msgstr "Следует ли разрешить пользователю запускать блокнот от имени root."
#: notebook/notebookapp.py:548
msgid "The default URL to redirect to from `/`"
msgstr "URL-адрес по умолчанию для перенаправления на `/`"
#: notebook/notebookapp.py:552
msgid "The IP address the notebook server will listen on."
msgstr "IP-адрес, который будет прослушиваться сервером блокнота."
#: notebook/notebookapp.py:565
#, python-format
msgid ""
"Cannot bind to localhost, using 127.0.0.1 as default ip\n"
"%s"
msgstr ""
"Невозможно привязаться к localhost, используйте 127.0.0.1 в качестве ip-"
"адреса по умолчанию\n"
"%s"
#: notebook/notebookapp.py:579
msgid "The port the notebook server will listen on."
msgstr "Порт сервера блокнотов для прослушивания."
#: notebook/notebookapp.py:583
msgid ""
"The number of additional ports to try if the specified port is not available."
msgstr ""
"Количество дополнительных портов, которые нужно попробовать, если указанный "
"порт недоступен."
#: notebook/notebookapp.py:587
msgid "The full path to an SSL/TLS certificate file."
msgstr "Полный путь к файлу сертификата с SSL/TLS."
#: notebook/notebookapp.py:591
msgid "The full path to a private key file for usage with SSL/TLS."
msgstr "Полный путь к файлу приватного ключа для использования с SSL/TLS."
#: notebook/notebookapp.py:595
msgid ""
"The full path to a certificate authority certificate for SSL/TLS client "
"authentication."
msgstr ""
"Полный путь к сертификату центра сертификации для SSL/TLS аутентификации "
"клиента."
#: notebook/notebookapp.py:599
msgid "The file where the cookie secret is stored."
msgstr "Файл, в котором хранится пароль cookie."
#: notebook/notebookapp.py:628
#, python-format
msgid "Writing notebook server cookie secret to %s"
msgstr "Запись пароля cookie сервера блокнота в %s"
#: notebook/notebookapp.py:635
#, python-format
msgid "Could not set permissions on %s"
msgstr "Не удалось установить разрешения для %s"
#: notebook/notebookapp.py:640
msgid ""
"Token used for authenticating first-time connections to the server.\n"
"\n"
" When no password is enabled,\n"
" the default is to generate a new, random token.\n"
"\n"
" Setting to an empty string disables authentication altogether, which "
"is NOT RECOMMENDED.\n"
" "
msgstr ""
"Токен, используемый для проверки подлинности при первом подключении к "
"серверу.\n"
"\n"
" Когда пароль не включен,\n"
" по умолчанию генерируется новый случайный токен.\n"
"\n"
" Установка пустой строки полностью отключает аутентификацию, что НЕ "
"РЕКОМЕНДУЕТСЯ.\n"
" "
#: notebook/notebookapp.py:650
msgid ""
"One-time token used for opening a browser.\n"
" Once used, this token cannot be used again.\n"
" "
msgstr ""
"Одноразовый токен, используемый для открытия в браузере. \n"
" После использования этот токен не может быть использован снова.\n"
" "
#: notebook/notebookapp.py:726
msgid ""
"Specify Where to open the notebook on startup. This is the\n"
" `new` argument passed to the standard library method `webbrowser."
"open`.\n"
" The behaviour is not guaranteed, but depends on browser support. "
"Valid\n"
" values are:\n"
" 2 opens a new tab,\n"
" 1 opens a new window,\n"
" 0 opens in an existing window.\n"
" See the `webbrowser.open` documentation for details.\n"
" "
msgstr ""
"Укажите где открывать блокнот при запуске. Это \"новый\"\n"
" аргумент, передаваемый для стандартного метода библиотек `webbrowser."
"open`.\n"
" Поведение не гарантируется, но зависит от поддержки браузера. "
"Допустимыми\n"
" значениями являются:\n"
" 2 открывает новую вкладку,\n"
" 1 открывает новое окно,\n"
" 0 открывается в существующем окне.\n"
" См. документацию `webbrowser.open` для получения подробной "
"информации.\n"
" "
#: notebook/notebookapp.py:737
msgid "DEPRECATED, use tornado_settings"
msgstr "УСТАРЕЛО - используйте tornado_settings"
#: notebook/notebookapp.py:742
msgid ""
"\n"
" webapp_settings is deprecated, use tornado_settings.\n"
msgstr ""
"\n"
" web app_settings устарел, используйте tornado_settings.\n"
#: notebook/notebookapp.py:746
msgid ""
"Supply overrides for the tornado.web.Application that the Jupyter notebook "
"uses."
msgstr ""
"Перекрывает поставки для tornado.web. Приложение, использующее блокнот "
"Jupyter."
#: notebook/notebookapp.py:750
msgid ""
"\n"
" Set the tornado compression options for websocket connections.\n"
"\n"
" This value will be returned from :meth:`WebSocketHandler."
"get_compression_options`.\n"
" None (default) will disable compression.\n"
" A dict (even an empty one) will enable compression.\n"
"\n"
" See the tornado docs for WebSocketHandler.get_compression_options "
"for details.\n"
" "
msgstr ""
"\n"
" Установите параметры сжатия tornado для соединений websocket.\n"
"\n"
" Это значение будет возвращено из :meth: 'WebSocketHandler."
"get_compression_options`.\n"
" None (по умолчанию) отключит сжатие.\n"
" Диктат (даже пустой) будет включать сжатие.\n"
"\n"
" Смотрите документы tornado для WebSocketHandler."
"get_compression_options для получения подробной информации.\n"
" "
#: notebook/notebookapp.py:761
msgid ""
"Supply overrides for terminado. Currently only supports \"shell_command\"."
msgstr ""
"Переопределение поставок для terminado. В настоящее время поддерживается "
"только \"shell_command\"."
#: notebook/notebookapp.py:764
msgid ""
"Extra keyword arguments to pass to `set_secure_cookie`. See tornado's "
"set_secure_cookie docs for details."
msgstr ""
"Дополнительные аргументы ключевого слова для передачи в `set_secure_cookie`. "
"Подробнее см. документацию set_secure_cookie для tornado."
#: notebook/notebookapp.py:768
msgid ""
"Supply SSL options for the tornado HTTPServer.\n"
" See the tornado docs for details."
msgstr ""
"Предоставьте параметры SSL для HTTPServer tornado.\n"
" Подробности смотрите в документации tornado."
#: notebook/notebookapp.py:772
msgid "Supply extra arguments that will be passed to Jinja environment."
msgstr "Предоставьте дополнительные аргументы, передаваемые в среду Jinja."
#: notebook/notebookapp.py:776
msgid "Extra variables to supply to jinja templates when rendering."
msgstr ""
"Дополнительные переменные для предоставления шаблонам jinja при рендеринге."
#: notebook/notebookapp.py:812
msgid "DEPRECATED use base_url"
msgstr "УСТАРЕЛО - используйте base_url"
#: notebook/notebookapp.py:816
msgid "base_project_url is deprecated, use base_url"
msgstr "base_project_url устарел - используйте base_url"
#: notebook/notebookapp.py:832
msgid "Path to search for custom.js, css"
msgstr "Путь для поиска custom.js, css"
#: notebook/notebookapp.py:844
msgid ""
"Extra paths to search for serving jinja templates.\n"
"\n"
" Can be used to override templates from notebook.templates."
msgstr ""
"Дополнительные пути для поиска обслуживающих шаблонов jinja.\n"
"\n"
" Может использоваться для переопределения шаблонов из notebook."
"templates."
#: notebook/notebookapp.py:855
msgid "extra paths to look for Javascript notebook extensions"
msgstr "дополнительные пути для поиска расширений блокнота Javascript"
#: notebook/notebookapp.py:900
#, python-format
msgid "Using MathJax: %s"
msgstr "Использование MathJax: %s"
#: notebook/notebookapp.py:903
msgid "The MathJax.js configuration file that is to be used."
msgstr "Использовать файл конфигурации MathJax.js."
#: notebook/notebookapp.py:908
#, python-format
msgid "Using MathJax configuration file: %s"
msgstr "Использование файла конфигурации MathJax: %s"
#: notebook/notebookapp.py:914
msgid "The notebook manager class to use."
msgstr "Используемый класс менеджера блокнотов."
#: notebook/notebookapp.py:920
msgid "The kernel manager class to use."
msgstr "Используемый класс менеджера ядра."
#: notebook/notebookapp.py:926
msgid "The session manager class to use."
msgstr "Класс менеджера сеансов для использования."
#: notebook/notebookapp.py:932
msgid "The config manager class to use"
msgstr "Класс менеджера конфигурации для использования"
#: notebook/notebookapp.py:953
msgid "The login handler class to use."
msgstr "Используемый класс обработчика входа в систему."
#: notebook/notebookapp.py:960
msgid "The logout handler class to use."
msgstr "Используемый класс обработчика выхода из системы."
#: notebook/notebookapp.py:964
msgid ""
"Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-"
"For headerssent by the upstream reverse proxy. Necessary if the proxy "
"handles SSL"
msgstr ""
"Стоит ли доверять или нет Х-схеме/х-перенаправлению-прото и X-реал-ip/х-"
"перенаправлению-для заголовков, загружаемых по потоку обратного прокси-"
"сервера. Необходимо, если прокси обрабатывает SSL"
#: notebook/notebookapp.py:976
msgid ""
"\n"
" DISABLED: use %pylab or %matplotlib in the notebook to enable "
"matplotlib.\n"
" "
msgstr ""
"\n"
" Отключено: используйте %pylab или %matplotlib в блокноте чтобы "
"включить matplotlib.\n"
" "
#: notebook/notebookapp.py:988
msgid "Support for specifying --pylab on the command line has been removed."
msgstr "Поддержка указания --pylab в командной строке была удалена."
#: notebook/notebookapp.py:990
msgid "Please use `%pylab{0}` or `%matplotlib{0}` in the notebook itself."
msgstr ""
"Пожалуйста, используйте `%pylab{0}` или `%matplotlib{0}` в самом блокноте."
#: notebook/notebookapp.py:995
msgid "The directory to use for notebooks and kernels."
msgstr "Каталог, используемый для блокнотов и ядер."
#: notebook/notebookapp.py:1018
#, python-format
msgid "No such notebook dir: '%r'"
msgstr "Нет такого блокнота: '%r'"
#: notebook/notebookapp.py:1031
msgid "DEPRECATED use the nbserver_extensions dict instead"
msgstr "УСТАРЕЛО - используйте nbserver_extensions вместо диктата"
#: notebook/notebookapp.py:1036
msgid "server_extensions is deprecated, use nbserver_extensions"
msgstr "server_extensions устарело - используйте nbserver_extensions"
#: notebook/notebookapp.py:1040
msgid ""
"Dict of Python modules to load as notebook server extensions.Entry values "
"can be used to enable and disable the loading ofthe extensions. The "
"extensions will be loaded in alphabetical order."
msgstr ""
"Dict модулей Python для загрузки в качестве серверных расширений блокнотов. "
"Введенные значения можно использовать для включения и отключения загрузки "
"расширений. Расширения будут загружены в алфавитном порядке."
#: notebook/notebookapp.py:1049
msgid "Reraise exceptions encountered loading server extensions?"
msgstr ""
"Повторно поднимаемые исключения, встречающиеся при загрузке серверных "
"расширений?"
#: notebook/notebookapp.py:1052
msgid ""
"(msgs/sec)\n"
" Maximum rate at which messages can be sent on iopub before they are\n"
" limited."
msgstr ""
"(сообщ/сек)\n"
" Максимальная скорость отправки сообщений на iopub, прежде\n"
" чем они будут ограничены."
#: notebook/notebookapp.py:1056
msgid ""
"(bytes/sec)\n"
" Maximum rate at which stream output can be sent on iopub before they "
"are\n"
" limited."
msgstr ""
"(байт/сек)\n"
" Максимальная скорость отправки потока вывода на iopub, прежде\n"
" чем он будет ограничен."
#: notebook/notebookapp.py:1060
msgid ""
"(sec) Time window used to \n"
" check the message and data rate limits."
msgstr ""
"(сек) Окно времени, используемое для \n"
" проверки ограничений скорости передачи сообщений и данных."
#: notebook/notebookapp.py:1071
#, python-format
msgid "No such file or directory: %s"
msgstr "Нет такого файла или каталога: %s"
#: notebook/notebookapp.py:1141
msgid "Notebook servers are configured to only be run with a password."
msgstr "Серверы блокнотов настроены на работу только с паролем."
#: notebook/notebookapp.py:1142
msgid "Hint: run the following command to set a password"
msgstr "Подсказка: выполните следующую команду, чтобы установить пароль"
#: notebook/notebookapp.py:1143
msgid "\t$ python -m notebook.auth password"
msgstr "\t$ python -m notebook.auth password"
#: notebook/notebookapp.py:1181
#, python-format
msgid "The port %i is already in use, trying another port."
msgstr "Порт %i уже используется, попробуйте другой порт."
#: notebook/notebookapp.py:1184
#, python-format
msgid "Permission to listen on port %i denied"
msgstr "Разрешение на прослушивание порта %i отклонено"
#: notebook/notebookapp.py:1193
msgid ""
"ERROR: the notebook server could not be started because no available port "
"could be found."
msgstr ""
"ОШИБКА: не удалось запустить сервер блокнота, так как не удалось найти "
"доступный порт."
#: notebook/notebookapp.py:1199
msgid "[all ip addresses on your system]"
msgstr "[все ip-адреса в вашей системе]"
#: notebook/notebookapp.py:1223
#, python-format
msgid "Terminals not available (error was %s)"
msgstr "Терминалы недоступны (ошибка: %s)"
#: notebook/notebookapp.py:1259
msgid "interrupted"
msgstr "прервано"
#: notebook/notebookapp.py:1261
msgid "y"
msgstr "д"
#: notebook/notebookapp.py:1262
msgid "n"
msgstr "н"
#: notebook/notebookapp.py:1263
#, python-format
msgid "Shutdown this notebook server (%s/[%s])? "
msgstr "Выключить этот сервер блокнотов (%s/[%s])? "
#: notebook/notebookapp.py:1269
msgid "Shutdown confirmed"
msgstr "Выключение подтверждено"
#: notebook/notebookapp.py:1273
msgid "No answer for 5s:"
msgstr "Нет ответа уже 5с:"
#: notebook/notebookapp.py:1274
msgid "resuming operation..."
msgstr "возобновить работу..."
#: notebook/notebookapp.py:1282
#, python-format
msgid "received signal %s, stopping"
msgstr "получен сигнал %s, остановка"
#: notebook/notebookapp.py:1338
#, python-format
msgid "Error loading server extension %s"
msgstr "Ошибка загрузки расширения сервера %s"
#: notebook/notebookapp.py:1369
#, python-format
msgid "Shutting down %d kernels"
msgstr "Выключить %d ядер"
#: notebook/notebookapp.py:1375
#, python-format
msgid "%d active kernel"
msgid_plural "%d active kernels"
msgstr[0] "%d активное ядро"
msgstr[1] "%d активных ядра"
msgstr[2] "%d активных ядер"
#: notebook/notebookapp.py:1379
#, python-format
msgid ""
"The Jupyter Notebook is running at:\n"
"\r%s"
msgstr ""
"Jupyter Notebook запущен на:\n"
"\r%s"
#: notebook/notebookapp.py:1426
msgid "Running as root is not recommended. Use --allow-root to bypass."
msgstr ""
"Запуск от имени root не рекомендуется. Используйте --allow-root для обхода."
#: notebook/notebookapp.py:1432
msgid ""
"Use Control-C to stop this server and shut down all kernels (twice to skip "
"confirmation)."
msgstr ""
"Используйте Control-C, для остановки этого сервера и выключения всех ядер "
"(дважды, чтобы пропустить подтверждение)."
#: notebook/notebookapp.py:1434
msgid ""
"Welcome to Project Jupyter! Explore the various tools available and their "
"corresponding documentation. If you are interested in contributing to the "
"platform, please visit the communityresources section at http://jupyter.org/"
"community.html."
msgstr ""
"Добро пожаловать в проект Jupyter! Изучите различные доступные инструменты и "
"соответствующую им документацию. Если вы заинтересованы в том, чтобы внести "
"свой вклад в платформу, пожалуйста, посетите раздел ресурсов сообщества по "
"адресу http://jupyter.org/community.html."
#: notebook/notebookapp.py:1445
#, python-format
msgid "No web browser found: %s."
msgstr "Веб-браузер не найден: %s."
#: notebook/notebookapp.py:1450
#, python-format
msgid "%s does not exist"
msgstr "%s не существует"
#: notebook/notebookapp.py:1484
msgid "Interrupted..."
msgstr "Прерывание..."
#: notebook/services/contents/filemanager.py:506
#, python-format
msgid "Serving notebooks from local directory: %s"
msgstr "Обслуживание блокнотов из локального каталога: %s"
#: notebook/services/contents/manager.py:68
msgid "Untitled"
msgstr "Без названия"

File diff suppressed because it is too large Load Diff

@ -1,733 +0,0 @@
# Translations template for Jupyter.
# Copyright (C) 2017 ORGANIZATION
# This file is distributed under the same license as the Jupyter project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
#
#, fuzzy
# Universal translation: cell=单元格; kernal=内核; don't translate notebook whenever referring to Jupyter Notebook
msgid ""
msgstr ""
"Project-Id-Version: Jupyter VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2017-08-25 02:53-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.5.0\n"
#: notebook/templates/404.html:3
msgid "You are requesting a page that does not exist!"
msgstr "您所请求的页面不存在!"
#: notebook/templates/edit.html:37
msgid "current mode"
msgstr "当前模式"
#: notebook/templates/edit.html:48 notebook/templates/notebook.html:78
msgid "File"
msgstr "文件"
#: notebook/templates/edit.html:50 notebook/templates/tree.html:57
msgid "New"
msgstr "新建"
#: notebook/templates/edit.html:51
msgid "Save"
msgstr "保存"
#: notebook/templates/edit.html:52 notebook/templates/tree.html:36
msgid "Rename"
msgstr "重命名"
#: notebook/templates/edit.html:53 notebook/templates/tree.html:38
msgid "Download"
msgstr "下载"
#: notebook/templates/edit.html:56 notebook/templates/notebook.html:131
#: notebook/templates/tree.html:41
msgid "Edit"
msgstr "编辑"
#: notebook/templates/edit.html:58
msgid "Find"
msgstr "查找"
#: notebook/templates/edit.html:59
msgid "Find &amp; Replace"
msgstr "查找 &amp; 替换"
#: notebook/templates/edit.html:61
msgid "Key Map"
msgstr "键值对"
#: notebook/templates/edit.html:62
msgid "Default"
msgstr "默认"
#: notebook/templates/edit.html:63
msgid "Sublime Text"
msgstr "代码编辑器"
#: notebook/templates/edit.html:68 notebook/templates/notebook.html:159
#: notebook/templates/tree.html:40
msgid "View"
msgstr "查看"
#: notebook/templates/edit.html:70 notebook/templates/notebook.html:162
msgid "Show/Hide the logo and notebook title (above menu bar)"
msgstr "显示/隐藏 标题和logo"
#: notebook/templates/edit.html:71 notebook/templates/notebook.html:163
msgid "Toggle Header"
msgstr "显示/隐藏 标题栏"
#: notebook/templates/edit.html:72 notebook/templates/notebook.html:171
msgid "Toggle Line Numbers"
msgstr "显示/隐藏 行号"
#: notebook/templates/edit.html:75
msgid "Language"
msgstr "语言"
#: notebook/templates/error.html:23
msgid "The error was:"
msgstr "错误:"
#: notebook/templates/login.html:24
msgid "Password or token:"
msgstr "密码或者token"
#: notebook/templates/login.html:26
msgid "Password:"
msgstr "密码:"
#: notebook/templates/login.html:31
msgid "Log in"
msgstr "登录"
#: notebook/templates/login.html:39
msgid "No login available, you shouldn't be seeing this page."
msgstr "还没有登录,请先登录。"
#: notebook/templates/logout.html:31
#, python-format
msgid "Proceed to the <a href=\"%(base_url)s\">dashboard"
msgstr "进入 <a href=\"%(base_url)s\"> 指示板"
#: notebook/templates/logout.html:33
#, python-format
msgid "Proceed to the <a href=\"%(base_url)slogin\">login page"
msgstr "进入 <a href=\"%(base_url)slogin\"> 登录页面"
#: notebook/templates/notebook.html:62
msgid "Menu"
msgstr "菜单"
#: notebook/templates/notebook.html:65 notebook/templates/notebook.html:254
msgid "Kernel"
msgstr "内核"
#: notebook/templates/notebook.html:68
msgid "This notebook is read-only"
msgstr "这个notebook文件是只读的"
#: notebook/templates/notebook.html:81
msgid "New Notebook"
msgstr "新建Notebook"
#: notebook/templates/notebook.html:85
msgid "Opens a new window with the Dashboard view"
msgstr "以仪表盘视角打开新的窗口"
#: notebook/templates/notebook.html:86
msgid "Open..."
msgstr "打开..."
#: notebook/templates/notebook.html:90
msgid "Open a copy of this notebook's contents and start a new kernel"
msgstr "复制并打开当前notebook的内容并启动一个新的内核"
#: notebook/templates/notebook.html:91
msgid "Make a Copy..."
msgstr "复制..."
#: notebook/templates/notebook.html:92
msgid "Rename..."
msgstr "重命名..."
#: notebook/templates/notebook.html:93
msgid "Save and Checkpoint"
msgstr "保存,且作为一个检查点"
#: notebook/templates/notebook.html:96
msgid "Revert to Checkpoint"
msgstr "恢复到某一个检查点"
#: notebook/templates/notebook.html:106
msgid "Print Preview"
msgstr "打印预览"
#: notebook/templates/notebook.html:107
msgid "Download as"
msgstr "另存为"
#: notebook/templates/notebook.html:109
msgid "Notebook (.ipynb)"
msgstr "Notebook (.ipynb)"
#: notebook/templates/notebook.html:110
msgid "Script"
msgstr "脚本"
#: notebook/templates/notebook.html:111
msgid "HTML (.html)"
msgstr ""
#: notebook/templates/notebook.html:112
msgid "Markdown (.md)"
msgstr ""
#: notebook/templates/notebook.html:113
msgid "reST (.rst)"
msgstr ""
#: notebook/templates/notebook.html:114
msgid "LaTeX (.tex)"
msgstr ""
#: notebook/templates/notebook.html:115
msgid "PDF via LaTeX (.pdf)"
msgstr "通过LaTeX生成的PDF (.pdf)"
#: notebook/templates/notebook.html:118
msgid "Deploy as"
msgstr "部署在"
#: notebook/templates/notebook.html:123
msgid "Trust the output of this notebook"
msgstr "信任当前notebook的输出"
#: notebook/templates/notebook.html:124
msgid "Trust Notebook"
msgstr "信任当前Notebook"
#: notebook/templates/notebook.html:127
msgid "Shutdown this notebook's kernel, and close this window"
msgstr "关闭当前notebook的内核并关闭当前窗口"
#: notebook/templates/notebook.html:128
msgid "Close and Halt"
msgstr "关闭"
#: notebook/templates/notebook.html:133
msgid "Cut Cells"
msgstr "剪切单元格"
#: notebook/templates/notebook.html:134
msgid "Copy Cells"
msgstr "复制单元格"
#: notebook/templates/notebook.html:135
msgid "Paste Cells Above"
msgstr "粘贴单元格到上面"
#: notebook/templates/notebook.html:136
msgid "Paste Cells Below"
msgstr "粘贴单元格到下面"
#: notebook/templates/notebook.html:137
msgid "Paste Cells &amp; Replace"
msgstr "粘贴单元格 &amp; 替换"
#: notebook/templates/notebook.html:138
msgid "Delete Cells"
msgstr "删除单元格"
#: notebook/templates/notebook.html:139
msgid "Undo Delete Cells"
msgstr "撤销删除单元格"
#: notebook/templates/notebook.html:141
msgid "Split Cell"
msgstr "分割单元格"
#: notebook/templates/notebook.html:142
msgid "Merge Cell Above"
msgstr "合并上面的单元格"
#: notebook/templates/notebook.html:143
msgid "Merge Cell Below"
msgstr "合并下面的单元格"
#: notebook/templates/notebook.html:145
msgid "Move Cell Up"
msgstr "上移单元格"
#: notebook/templates/notebook.html:146
msgid "Move Cell Down"
msgstr "下移单元格"
#: notebook/templates/notebook.html:148
msgid "Edit Notebook Metadata"
msgstr "编辑Notebook的元数据"
#: notebook/templates/notebook.html:150
msgid "Find and Replace"
msgstr "查找并替换"
#: notebook/templates/notebook.html:152
msgid "Cut Cell Attachments"
msgstr "剪切附件"
#: notebook/templates/notebook.html:153
msgid "Copy Cell Attachments"
msgstr "复制附件"
#: notebook/templates/notebook.html:154
msgid "Paste Cell Attachments"
msgstr "粘贴附件"
#: notebook/templates/notebook.html:156
msgid "Insert Image"
msgstr "插入图片"
#: notebook/templates/notebook.html:166
msgid "Show/Hide the action icons (below menu bar)"
msgstr "显示/隐藏 操作图标"
#: notebook/templates/notebook.html:167
msgid "Toggle Toolbar"
msgstr "显示/隐藏 工具栏"
#: notebook/templates/notebook.html:170
msgid "Show/Hide line numbers in cells"
msgstr "显示/隐藏 当前单元格的行号"
#: notebook/templates/notebook.html:174
msgid "Cell Toolbar"
msgstr "单元格 工具栏"
#: notebook/templates/notebook.html:179
msgid "Insert"
msgstr "插入"
#: notebook/templates/notebook.html:182
msgid "Insert an empty Code cell above the currently active cell"
msgstr "在当前代码块上面插入一个空的单元格"
#: notebook/templates/notebook.html:183
msgid "Insert Cell Above"
msgstr "在上面插入一个单元格"
#: notebook/templates/notebook.html:185
msgid "Insert an empty Code cell below the currently active cell"
msgstr "在当前代码块下面插入一个空的单元格"
#: notebook/templates/notebook.html:186
msgid "Insert Cell Below"
msgstr "在下面插入一个单元格"
#: notebook/templates/notebook.html:189
msgid "Cell"
msgstr "单元格"
#: notebook/templates/notebook.html:191
msgid "Run this cell, and move cursor to the next one"
msgstr "运行此单元格,并将光标移到下一个"
#: notebook/templates/notebook.html:192
msgid "Run Cells"
msgstr "运行所有单元格"
#: notebook/templates/notebook.html:193
msgid "Run this cell, select below"
msgstr "运行此单元格,并自动选择下一个"
#: notebook/templates/notebook.html:194
msgid "Run Cells and Select Below"
msgstr "运行所有单元格,并自动选择下一个"
#: notebook/templates/notebook.html:195
msgid "Run this cell, insert below"
msgstr "运行此单元格,并在下面插入一个新的单元格"
#: notebook/templates/notebook.html:196
msgid "Run Cells and Insert Below"
msgstr "运行所有单元格,并在下面插入一个新的单元格"
#: notebook/templates/notebook.html:197
msgid "Run all cells in the notebook"
msgstr "运行所有的单元格"
#: notebook/templates/notebook.html:198
msgid "Run All"
msgstr "运行所有"
#: notebook/templates/notebook.html:199
msgid "Run all cells above (but not including) this cell"
msgstr "运行上面所有的单元格(但不包括这个单元格)"
#: notebook/templates/notebook.html:200
msgid "Run All Above"
msgstr "运行上面所有的单元格"
#: notebook/templates/notebook.html:201
msgid "Run this cell and all cells below it"
msgstr "运行当前及以下所有的单元格"
#: notebook/templates/notebook.html:202
msgid "Run All Below"
msgstr "运行下面的所有的单元格"
#: notebook/templates/notebook.html:205
msgid "All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells"
msgstr "Notebook里的所有单元格都有一个类型。默认新单元格都会被创建为代码单元格"
#: notebook/templates/notebook.html:206
msgid "Cell Type"
msgstr "单元格类型"
#: notebook/templates/notebook.html:209
msgid "Contents will be sent to the kernel for execution, and output will display in the footer of cell"
msgstr "内容将被发送到内核执行,输出将显示在单元格的页脚"
#: notebook/templates/notebook.html:212
msgid "Contents will be rendered as HTML and serve as explanatory text"
msgstr "内容将以HTML形式呈现, 并作为解释性文本"
#: notebook/templates/notebook.html:213 notebook/templates/notebook.html:298
msgid "Markdown"
msgstr ""
#: notebook/templates/notebook.html:215
msgid "Contents will pass through nbconvert unmodified"
msgstr "内容将通过nbconvert且不会被修改"
#: notebook/templates/notebook.html:216
msgid "Raw NBConvert"
msgstr "原生 NBConvert"
#: notebook/templates/notebook.html:220
msgid "Current Outputs"
msgstr "当前输出"
#: notebook/templates/notebook.html:223
msgid "Hide/Show the output of the current cell"
msgstr "隐藏/显示当前单元格输出"
#: notebook/templates/notebook.html:224 notebook/templates/notebook.html:240
msgid "Toggle"
msgstr "显示/隐藏"
#: notebook/templates/notebook.html:227
msgid "Scroll the output of the current cell"
msgstr "滚动到当前单元格的输出"
#: notebook/templates/notebook.html:228 notebook/templates/notebook.html:244
msgid "Toggle Scrolling"
msgstr "滚动显示"
#: notebook/templates/notebook.html:231
msgid "Clear the output of the current cell"
msgstr "清除当前单元格的输出"
#: notebook/templates/notebook.html:232 notebook/templates/notebook.html:248
msgid "Clear"
msgstr "清空"
#: notebook/templates/notebook.html:236
msgid "All Output"
msgstr "所有输出"
#: notebook/templates/notebook.html:239
msgid "Hide/Show the output of all cells"
msgstr "隐藏/显示 所有单元格的输出"
#: notebook/templates/notebook.html:243
msgid "Scroll the output of all cells"
msgstr "切换所有单元格的输出为滚动模式"
#: notebook/templates/notebook.html:247
msgid "Clear the output of all cells"
msgstr "清空所有代码块的输出"
#: notebook/templates/notebook.html:257
msgid "Send Keyboard Interrupt (CTRL-C) to the Kernel"
msgstr "按下 CTRL-C 中断内核"
#: notebook/templates/notebook.html:258
msgid "Interrupt"
msgstr "中断"
#: notebook/templates/notebook.html:261
msgid "Restart the Kernel"
msgstr "重启内核"
#: notebook/templates/notebook.html:262
msgid "Restart"
msgstr "重启"
#: notebook/templates/notebook.html:265
msgid "Restart the Kernel and clear all output"
msgstr "重启服务并清空所有输出"
#: notebook/templates/notebook.html:266
msgid "Restart &amp; Clear Output"
msgstr "重启 &amp; 清空输出"
#: notebook/templates/notebook.html:269
msgid "Restart the Kernel and re-run the notebook"
msgstr "重启内核并且重新运行整个notebook"
#: notebook/templates/notebook.html:270
msgid "Restart &amp; Run All"
msgstr "重启 &amp; 运行所有"
#: notebook/templates/notebook.html:273
msgid "Reconnect to the Kernel"
msgstr "重连内核"
#: notebook/templates/notebook.html:274
msgid "Reconnect"
msgstr "重连"
#: notebook/templates/notebook.html:282
msgid "Change kernel"
msgstr "更换内核"
#: notebook/templates/notebook.html:287
msgid "Help"
msgstr "帮助"
#: notebook/templates/notebook.html:290
msgid "A quick tour of the notebook user interface"
msgstr "快速浏览一下notebook用户界面"
#: notebook/templates/notebook.html:290
msgid "User Interface Tour"
msgstr "用户界面之旅"
#: notebook/templates/notebook.html:291
msgid "Opens a tooltip with all keyboard shortcuts"
msgstr "打开包含所有快捷键的提示信息"
#: notebook/templates/notebook.html:291
msgid "Keyboard Shortcuts"
msgstr "快捷键"
#: notebook/templates/notebook.html:292
msgid "Opens a dialog allowing you to edit Keyboard shortcuts"
msgstr "打开对话框编辑快捷键"
#: notebook/templates/notebook.html:292
msgid "Edit Keyboard Shortcuts"
msgstr "编辑快捷键"
#: notebook/templates/notebook.html:297
msgid "Notebook Help"
msgstr "帮助"
#: notebook/templates/notebook.html:303
msgid "Opens in a new window"
msgstr "在新窗口打开"
#: notebook/templates/notebook.html:319
msgid "About Jupyter Notebook"
msgstr "关于本程序"
#: notebook/templates/notebook.html:319
msgid "About"
msgstr "关于"
#: notebook/templates/page.html:114
msgid "Jupyter Notebook requires JavaScript."
msgstr "Jupyter Notebook 需要 JavaScript。"
#: notebook/templates/page.html:115
msgid "Please enable it to proceed. "
msgstr "请启用它以继续。"
#: notebook/templates/page.html:122
msgid "dashboard"
msgstr "指示板"
#: notebook/templates/page.html:135
msgid "Logout"
msgstr "注销"
#: notebook/templates/page.html:137
msgid "Login"
msgstr "登录"
#: notebook/templates/tree.html:23
msgid "Files"
msgstr "文件"
#: notebook/templates/tree.html:24
msgid "Running"
msgstr "运行"
#: notebook/templates/tree.html:25
msgid "Clusters"
msgstr "集群"
#: notebook/templates/tree.html:32
msgid "Select items to perform actions on them."
msgstr "选择操作对象."
#: notebook/templates/tree.html:35
msgid "Duplicate selected"
msgstr "复制选中的对象"
#: notebook/templates/tree.html:35
msgid "Duplicate"
msgstr "复制"
#: notebook/templates/tree.html:36
msgid "Rename selected"
msgstr "重命名选中的对象"
#: notebook/templates/tree.html:37
msgid "Move selected"
msgstr "移动选中的对象"
#: notebook/templates/tree.html:37
msgid "Move"
msgstr "移动"
#: notebook/templates/tree.html:38
msgid "Download selected"
msgstr "下载选中的对象"
#: notebook/templates/tree.html:39
msgid "Shutdown selected notebook(s)"
msgstr "停止运行选择的notebook(s)"
#: notebook/templates/notebook.html:278
#: notebook/templates/tree.html:39
msgid "Shutdown"
msgstr "关闭"
#: notebook/templates/tree.html:40
msgid "View selected"
msgstr "查看选中的对象"
#: notebook/templates/tree.html:41
msgid "Edit selected"
msgstr "编辑选中的对象"
#: notebook/templates/tree.html:42
msgid "Delete selected"
msgstr "删除选中的对象"
#: notebook/templates/tree.html:50
msgid "Click to browse for a file to upload."
msgstr "浏览文件并上传"
#: notebook/templates/tree.html:51
msgid "Upload"
msgstr "上传"
#: notebook/templates/tree.html:65
msgid "Text File"
msgstr "文本文件"
#: notebook/templates/tree.html:68
msgid "Folder"
msgstr "文件夹"
#: notebook/templates/tree.html:72
msgid "Terminal"
msgstr "终端"
#: notebook/templates/tree.html:76
msgid "Terminals Unavailable"
msgstr "终端不可用"
#: notebook/templates/tree.html:82
msgid "Refresh notebook list"
msgstr "刷新笔记列表"
#: notebook/templates/tree.html:90
msgid "Select All / None"
msgstr "全选 / 全不选"
#: notebook/templates/tree.html:93
msgid "Select..."
msgstr "选择..."
#: notebook/templates/tree.html:98
msgid "Select All Folders"
msgstr "选择所有文件夹"
#: notebook/templates/tree.html:98
msgid "Folders"
msgstr "文件夹"
#: notebook/templates/tree.html:99
msgid "Select All Notebooks"
msgstr "选择所有Notebooks"
#: notebook/templates/tree.html:99
msgid "All Notebooks"
msgstr "所有Notebooks"
#: notebook/templates/tree.html:100
msgid "Select Running Notebooks"
msgstr "选择运行中的Notebooks"
#: notebook/templates/tree.html:100
msgid "Running"
msgstr "运行"
#: notebook/templates/tree.html:101
msgid "Select All Files"
msgstr "选择所有文件"
#: notebook/templates/tree.html:101
msgid "Files"
msgstr "文件"
#: notebook/templates/tree.html:114
msgid "Last Modified"
msgstr "最后修改"
#: notebook/templates/tree.html:120
msgid "Name"
msgstr "名字"
#: notebook/templates/tree.html:130
msgid "Currently running Jupyter processes"
msgstr "当前运行Jupyter"
#: notebook/templates/tree.html:134
msgid "Refresh running list"
msgstr "刷新运行列表"
#: notebook/templates/tree.html:150
msgid "There are no terminals running."
msgstr "没有终端正在运行。"
#: notebook/templates/tree.html:152
msgid "Terminals are unavailable."
msgstr "终端不可用。"
#: notebook/templates/tree.html:162
msgid "Notebooks"
msgstr ""
#: notebook/templates/tree.html:169
msgid "There are no notebooks running."
msgstr "没有notebooks正在运行。"
#: notebook/templates/tree.html:178
msgid "Clusters tab is now provided by IPython parallel."
msgstr "集群标签现在由IPython并行提供。"
#: notebook/templates/tree.html:179
msgid "See '<a href=\"https://github.com/ipython/ipyparallel\">IPython parallel</a>' for installation details."
msgstr "安装细节查看 '<a href=\"https://github.com/ipython/ipyparallel\">IPython parallel</a>'。"

@ -1,486 +0,0 @@
# Translations template for Jupyter.
# Copyright (C) 2017 ORGANIZATION
# This file is distributed under the same license as the Jupyter project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
#
#, fuzzy
# Universal translation: cell=单元格; kernal=内核; don't translate notebook whenever referring to Jupyter Notebook
msgid ""
msgstr ""
"Project-Id-Version: Jupyter VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2017-08-25 02:53-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.5.0\n"
#: notebook/notebookapp.py:49
msgid "The Jupyter Notebook requires tornado >= 4.0"
msgstr "Jupyter Notebook要求拥有 tornado 版本号 >= 4.0"
#: notebook/notebookapp.py:53
msgid "The Jupyter Notebook requires tornado >= 4.0, but you have < 1.1.0"
msgstr "Jupyter Notebook要求拥有 tornado 版本号 >= 4.0, 但你的版本号却是 < 1.1.0"
#: notebook/notebookapp.py:55
#, python-format
msgid "The Jupyter Notebook requires tornado >= 4.0, but you have %s"
msgstr "Jupyter Notebook要求拥有 tornado 版本号 >= 4.0, 但你的版本号却是 %s"
#: notebook/notebookapp.py:205
msgid "The `ignore_minified_js` flag is deprecated and no longer works."
msgstr "`ignore_minified_js` 这个标志已经被弃用且不再工作。"
#: notebook/notebookapp.py:206
#, python-format
msgid "Alternatively use `%s` when working on the notebook's Javascript and LESS"
msgstr "在使用notebook的JavaScript和LESS时可以替换使用 `%s` "
#: notebook/notebookapp.py:207
msgid "The `ignore_minified_js` flag is deprecated and will be removed in Notebook 6.0"
msgstr "`ignore_minified_js` 这个标志已经被弃用且会在Notebook 6.0版本时被移除"
#: notebook/notebookapp.py:385
msgid "List currently running notebook servers."
msgstr "列出当前运行的Notebook服务。"
#: notebook/notebookapp.py:389
msgid "Produce machine-readable JSON list output."
msgstr "生成机器可读的JSON输出。"
#: notebook/notebookapp.py:391
msgid "Produce machine-readable JSON object on each line of output."
msgstr "在每一行输出上生成机器可读的JSON。"
#: notebook/notebookapp.py:395
msgid "If True, the output will be a JSON list of objects, one per active notebook server, each with the details from the relevant server info file."
msgstr "如果是正确的输出将是一个对象的JSON列表每个活动的笔记本服务器都有一个每一个都有相关的服务器信息文件的详细信息。"
#: notebook/notebookapp.py:399
msgid "If True, each line of output will be a JSON object with the details from the server info file. For a JSON list output, see the NbserverListApp.jsonlist configuration value"
msgstr "如果是正确的每一行输出将是一个JSON对象且包含来自服务器信息文件的详细信息。对于一个JSON列表输出请参阅NbserverListApp.jsonlist配置值"
#: notebook/notebookapp.py:425
msgid "Don't open the notebook in a browser after startup."
msgstr "请不要在启动服务以后在浏览器中打开notebook。"
#: notebook/notebookapp.py:429
msgid "DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib."
msgstr "尚未启用请在notebook中使用 %pylab or %matplotlib 指令来启用 matplotlib 包。"
#: notebook/notebookapp.py:445
msgid "Allow the notebook to be run from root user."
msgstr "允许notebook在root用户下运行。"
#: notebook/notebookapp.py:476
msgid ""
"The Jupyter HTML Notebook.\n"
" \n"
" This launches a Tornado based HTML Notebook Server that serves up an HTML5/Javascript Notebook client."
msgstr "The Jupyter HTML Notebook.\n \n 这将启动一个基于tornado的HTML Notebook服务器并提供一个HTML5/Javascript Notebook客户端。"
#: notebook/notebookapp.py:515
msgid "Deprecated: Use minified JS file or not, mainly use during dev to avoid JS recompilation"
msgstr ""
#: notebook/notebookapp.py:546
msgid "Set the Access-Control-Allow-Credentials: true header"
msgstr "请把Access-Control-Allow-Credentials设置为true header"
#: notebook/notebookapp.py:550
msgid "Whether to allow the user to run the notebook as root."
msgstr "是否允许notebook在root用户下运行。"
#: notebook/notebookapp.py:554
msgid "The default URL to redirect to from `/`"
msgstr "从 `/` 重定向到的默认URL "
#: notebook/notebookapp.py:558
msgid "The IP address the notebook server will listen on."
msgstr "notebook服务会监听的IP地址。"
#: notebook/notebookapp.py:571
#, python-format
msgid ""
"Cannot bind to localhost, using 127.0.0.1 as default ip\n"
"%s"
msgstr "不能绑定到localhost, 请使用127.0.0.1作为默认的IP \n %s"
#: notebook/notebookapp.py:585
msgid "The port the notebook server will listen on."
msgstr "notebook服务会监听的IP端口。"
#: notebook/notebookapp.py:589
msgid "The number of additional ports to try if the specified port is not available."
msgstr "如果指定的端口不可用,则要尝试其他端口的数量。"
#: notebook/notebookapp.py:593
msgid "The full path to an SSL/TLS certificate file."
msgstr "SSL/TLS 认证文件所在的完整路径。"
#: notebook/notebookapp.py:597
msgid "The full path to a private key file for usage with SSL/TLS."
msgstr "SSL/TLS 私钥文件所在的完整路径。"
#: notebook/notebookapp.py:601
msgid "The full path to a certificate authority certificate for SSL/TLS client authentication."
msgstr "SSL/TLS 客户端身份验证的证书所在的完整路径。"
#: notebook/notebookapp.py:605
msgid "The file where the cookie secret is stored."
msgstr "那个存放cookie密钥的文件。"
#: notebook/notebookapp.py:634
#, python-format
msgid "Writing notebook server cookie secret to %s"
msgstr "把notebook服务器的cookie密码写入 %s"
#: notebook/notebookapp.py:641
#, python-format
msgid "Could not set permissions on %s"
msgstr "无法在 %s 设置权限"
#: notebook/notebookapp.py:646
msgid ""
"Token used for authenticating first-time connections to the server.\n"
"\n"
" When no password is enabled,\n"
" the default is to generate a new, random token.\n"
"\n"
" Setting to an empty string disables authentication altogether, which is NOT RECOMMENDED.\n"
" "
msgstr "服务器第一次连接所需要的认证秘钥。\n \n 如果没有弃用密码,\n 默认将生成一个新的随机秘钥。\n \n 由于设置为一个空白字段会自动关闭认证,我们不推荐该行为。\n"
#: notebook/notebookapp.py:656
msgid ""
"One-time token used for opening a browser.\n"
" Once used, this token cannot be used again.\n"
" "
msgstr "打开浏览器的一次性秘钥。\n 一旦使用,则无法再次使用。\n"
#: notebook/notebookapp.py:732
msgid ""
"Specify Where to open the notebook on startup. This is the\n"
" `new` argument passed to the standard library method `webbrowser.open`.\n"
" The behaviour is not guaranteed, but depends on browser support. Valid\n"
" values are:\n"
" 2 opens a new tab,\n"
" 1 opens a new window,\n"
" 0 opens in an existing window.\n"
" See the `webbrowser.open` documentation for details.\n"
" "
msgstr ""
#: notebook/notebookapp.py:743
msgid "DEPRECATED, use tornado_settings"
msgstr "已被弃用!请使用 tornado_settings"
#: notebook/notebookapp.py:748
msgid ""
"\n"
" webapp_settings is deprecated, use tornado_settings.\n"
msgstr "webapp_settings已被弃用请使用tornado_settings。"
#: notebook/notebookapp.py:752
msgid "Supply overrides for the tornado.web.Application that the Jupyter notebook uses."
msgstr "Supply重写了Jupyter notebook正在使用的tornado.web.Application。"
#: notebook/notebookapp.py:756
msgid ""
"\n"
" Set the tornado compression options for websocket connections.\n"
"\n"
" This value will be returned from :meth:`WebSocketHandler.get_compression_options`.\n"
" None (default) will disable compression.\n"
" A dict (even an empty one) will enable compression.\n"
"\n"
" See the tornado docs for WebSocketHandler.get_compression_options for details.\n"
" "
msgstr ""
#: notebook/notebookapp.py:767
msgid "Supply overrides for terminado. Currently only supports \"shell_command\"."
msgstr ""
#: notebook/notebookapp.py:770
msgid "Extra keyword arguments to pass to `set_secure_cookie`. See tornado's set_secure_cookie docs for details."
msgstr ""
#: notebook/notebookapp.py:774
msgid ""
"Supply SSL options for the tornado HTTPServer.\n"
" See the tornado docs for details."
msgstr ""
#: notebook/notebookapp.py:778
msgid "Supply extra arguments that will be passed to Jinja environment."
msgstr "请添加Jinja环境所需要的额外的变量。"
#: notebook/notebookapp.py:782
msgid "Extra variables to supply to jinja templates when rendering."
msgstr "需要额外的变量去渲染Jinja模板。"
#: notebook/notebookapp.py:818
msgid "DEPRECATED use base_url"
msgstr "已被弃用!请使用 base_url"
#: notebook/notebookapp.py:822
msgid "base_project_url is deprecated, use base_url"
msgstr "base_project_url已被弃用请使用 base_url"
#: notebook/notebookapp.py:838
msgid "Path to search for custom.js, css"
msgstr "搜索custom.js, css所在的路径"
#: notebook/notebookapp.py:850
msgid ""
"Extra paths to search for serving jinja templates.\n"
"\n"
" Can be used to override templates from notebook.templates."
msgstr "额外寻找Jinja模板的路径。\n \n 可以被用来覆盖 notebook.templates 里的模板"
#: notebook/notebookapp.py:861
msgid "extra paths to look for Javascript notebook extensions"
msgstr "额外寻找Javascript notebook扩展的路径"
#: notebook/notebookapp.py:906
#, python-format
msgid "Using MathJax: %s"
msgstr ""
#: notebook/notebookapp.py:909
msgid "The MathJax.js configuration file that is to be used."
msgstr ""
#: notebook/notebookapp.py:914
#, python-format
msgid "Using MathJax configuration file: %s"
msgstr ""
#: notebook/notebookapp.py:920
msgid "The notebook manager class to use."
msgstr ""
#: notebook/notebookapp.py:926
msgid "The kernel manager class to use."
msgstr ""
#: notebook/notebookapp.py:932
msgid "The session manager class to use."
msgstr ""
#: notebook/notebookapp.py:938
msgid "The config manager class to use"
msgstr ""
#: notebook/notebookapp.py:959
msgid "The login handler class to use."
msgstr ""
#: notebook/notebookapp.py:966
msgid "The logout handler class to use."
msgstr ""
#: notebook/notebookapp.py:970
msgid "Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headerssent by the upstream reverse proxy. Necessary if the proxy handles SSL"
msgstr ""
#: notebook/notebookapp.py:982
msgid ""
"\n"
" DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib.\n"
" "
msgstr "\n 尚未启用请在Notebook里使用 %pylab or %matplotlib 指令去启用matplotlib。\n"
#: notebook/notebookapp.py:994
msgid "Support for specifying --pylab on the command line has been removed."
msgstr "不再支持在命令行里使用 --pylab 指令。"
#: notebook/notebookapp.py:996
msgid "Please use `%pylab{0}` or `%matplotlib{0}` in the notebook itself."
msgstr "请在notebook里使用 %pylab{0}` or `%matplotlib{0}`。"
#: notebook/notebookapp.py:1001
msgid "The directory to use for notebooks and kernels."
msgstr "用于notebooks和内核的目录。"
#: notebook/notebookapp.py:1024
#, python-format
msgid "No such notebook dir: '%r'"
msgstr "没有找到notebook的路径: '%r' "
#: notebook/notebookapp.py:1037
msgid "DEPRECATED use the nbserver_extensions dict instead"
msgstr "已被弃用!请使用 nbserver_extensions dict 指令"
#: notebook/notebookapp.py:1042
msgid "server_extensions is deprecated, use nbserver_extensions"
msgstr "server_extensions指令已被弃用请使用nbserver_extensions"
#: notebook/notebookapp.py:1046
msgid "Dict of Python modules to load as notebook server extensions.Entry values can be used to enable and disable the loading ofthe extensions. The extensions will be loaded in alphabetical order."
msgstr "可被作为notebook服务器扩展的Python模块们。可以使用条目值来启用和禁用扩展的加载。这些扩展将以字母顺序加载。"
#: notebook/notebookapp.py:1055
msgid "Reraise exceptions encountered loading server extensions?"
msgstr "加载服务器扩展时,遇到重新引发的异常?"
#: notebook/notebookapp.py:1058
msgid ""
"(msgs/sec)\n"
" Maximum rate at which messages can be sent on iopub before they are\n"
" limited."
msgstr ""
#: notebook/notebookapp.py:1062
msgid ""
"(bytes/sec)\n"
" Maximum rate at which stream output can be sent on iopub before they are\n"
" limited."
msgstr ""
#: notebook/notebookapp.py:1066
msgid ""
"(sec) Time window used to \n"
" check the message and data rate limits."
msgstr "(sec)时间窗口被用来 \n 检查消息和限制数据速率。"
#: notebook/notebookapp.py:1077
#, python-format
msgid "No such file or directory: %s"
msgstr "找不到文件或文件夹: %s"
#: notebook/notebookapp.py:1147
msgid "Notebook servers are configured to only be run with a password."
msgstr "Notebook服务器被设置为只能使用密码运行。"
#: notebook/notebookapp.py:1148
msgid "Hint: run the following command to set a password"
msgstr "提示: 运行下面命令设置密码"
#: notebook/notebookapp.py:1149
msgid "\t$ python -m notebook.auth password"
msgstr "\t$ python -m notebook.auth password"
#: notebook/notebookapp.py:1187
#, python-format
msgid "The port %i is already in use, trying another port."
msgstr "端口 %i 已经被占用, 请尝试其他端口。"
#: notebook/notebookapp.py:1190
#, python-format
msgid "Permission to listen on port %i denied"
msgstr "没有监听端口 %i 的权限"
#: notebook/notebookapp.py:1199
msgid "ERROR: the notebook server could not be started because no available port could be found."
msgstr "错误: Notebook服务器启动失败 - 因为没有找到可用的端口。"
#: notebook/notebookapp.py:1205
msgid "[all ip addresses on your system]"
msgstr "[系统所有IP地址]"
#: notebook/notebookapp.py:1229
#, python-format
msgid "Terminals not available (error was %s)"
msgstr "终端不可用(错误: %s)"
#: notebook/notebookapp.py:1265
msgid "interrupted"
msgstr "中断"
#: notebook/notebookapp.py:1267
msgid "y"
msgstr ""
#: notebook/notebookapp.py:1268
msgid "n"
msgstr ""
#: notebook/notebookapp.py:1269
#, python-format
msgid "Shutdown this notebook server (%s/[%s])? "
msgstr "关闭这个notebook服务器 (%s/[%s])"
#: notebook/notebookapp.py:1275
msgid "Shutdown confirmed"
msgstr "确认关闭"
#: notebook/notebookapp.py:1279
msgid "No answer for 5s:"
msgstr "5秒未响应"
#: notebook/notebookapp.py:1280
msgid "resuming operation..."
msgstr "重启操作..."
#: notebook/notebookapp.py:1288
#, python-format
msgid "received signal %s, stopping"
msgstr "接受信号 %s, 正在停止"
#: notebook/notebookapp.py:1344
#, python-format
msgid "Error loading server extension %s"
msgstr "加载插件 %s 失败"
#: notebook/notebookapp.py:1375
#, python-format
msgid "Shutting down %d kernel"
msgid_plural "Shutting down %d kernels"
msgstr[0] "关闭 %d 内核"
msgstr[1] "关闭 %d 内核"
#: notebook/notebookapp.py:1383
#, python-format
msgid "%d active kernel"
msgid_plural "%d active kernels"
msgstr[0] "%d 活跃的内核"
msgstr[1] "%d 活跃的内核"
#: notebook/notebookapp.py:1387
#, python-format
msgid ""
"The Jupyter Notebook is running at:\n"
"%s"
msgstr "Jupyter Notebook运行在:\n %s"
#: notebook/notebookapp.py:1434
msgid "Running as root is not recommended. Use --allow-root to bypass."
msgstr "不建议以root身份运行。请使用 --allow-root 绕过。"
#: notebook/notebookapp.py:1440
msgid "Use Control-C to stop this server and shut down all kernels (twice to skip confirmation)."
msgstr "使用 Control-C 停止此服务器并关闭所有内核(连续操作两次便可跳过确认界面)。"
#: notebook/notebookapp.py:1442
msgid "Welcome to Project Jupyter! Explore the various tools available and their corresponding documentation. If you are interested in contributing to the platform, please visit the communityresources section at http://jupyter.org/community.html."
msgstr "欢迎来到Jupyter项目! 探索可用的各种工具及其相应的文档。如果你有兴趣对这个平台请访问http://jupyter.org/community.html 的community resources部分。"
#: notebook/notebookapp.py:1453
#, python-format
msgid "No web browser found: %s."
msgstr "没有找到网页浏览器: %s。"
#: notebook/notebookapp.py:1458
#, python-format
msgid "%s does not exist"
msgstr "%s 不存在"
#: notebook/notebookapp.py:1492
msgid "Interrupted..."
msgstr "已经中断..."
#: notebook/services/contents/filemanager.py:525
#, python-format
msgid "Serving notebooks from local directory: %s"
msgstr "notebooks 运行所在的本地路径: %s"
#: notebook/services/contents/manager.py:69
msgid "Untitled"
msgstr "未命名"

@ -1,615 +0,0 @@
"""Notebook Javascript Test Controller
This module runs one or more subprocesses which will actually run the Javascript
test suite.
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import argparse
import json
import multiprocessing.pool
import os
import re
import requests
import signal
import sys
import subprocess
import time
from io import BytesIO
from threading import Thread, Lock, Event
from unittest.mock import patch
from jupyter_core.paths import jupyter_runtime_dir
from ipython_genutils.py3compat import bytes_to_str, which
from notebook._sysinfo import get_sys_info
from ipython_genutils.tempdir import TemporaryDirectory
from subprocess import TimeoutExpired
def popen_wait(p, timeout):
return p.wait(timeout)
NOTEBOOK_SHUTDOWN_TIMEOUT = 10
have = {}
have['casperjs'] = bool(which('casperjs'))
have['phantomjs'] = bool(which('phantomjs'))
have['slimerjs'] = bool(which('slimerjs'))
class StreamCapturer(Thread):
daemon = True # Don't hang if main thread crashes
started = False
def __init__(self, echo=False):
super().__init__()
self.echo = echo
self.streams = []
self.buffer = BytesIO()
self.readfd, self.writefd = os.pipe()
self.buffer_lock = Lock()
self.stop = Event()
def run(self):
self.started = True
while not self.stop.is_set():
chunk = os.read(self.readfd, 1024)
with self.buffer_lock:
self.buffer.write(chunk)
if self.echo:
sys.stdout.write(bytes_to_str(chunk))
os.close(self.readfd)
os.close(self.writefd)
def reset_buffer(self):
with self.buffer_lock:
self.buffer.truncate(0)
self.buffer.seek(0)
def get_buffer(self):
with self.buffer_lock:
return self.buffer.getvalue()
def ensure_started(self):
if not self.started:
self.start()
def halt(self):
"""Safely stop the thread."""
if not self.started:
return
self.stop.set()
os.write(self.writefd, b'\0') # Ensure we're not locked in a read()
self.join()
class TestController(object):
"""Run tests in a subprocess
"""
#: str, test group to be executed.
section = None
#: list, command line arguments to be executed
cmd = None
#: dict, extra environment variables to set for the subprocess
env = None
#: list, TemporaryDirectory instances to clear up when the process finishes
dirs = None
#: subprocess.Popen instance
process = None
#: str, process stdout+stderr
stdout = None
def __init__(self):
self.cmd = []
self.env = {}
self.dirs = []
def setup(self):
"""Create temporary directories etc.
This is only called when we know the test group will be run. Things
created here may be cleaned up by self.cleanup().
"""
pass
def launch(self, buffer_output=False, capture_output=False):
# print('*** ENV:', self.env) # dbg
# print('*** CMD:', self.cmd) # dbg
env = os.environ.copy()
env.update(self.env)
if buffer_output:
capture_output = True
self.stdout_capturer = c = StreamCapturer(echo=not buffer_output)
c.start()
stdout = c.writefd if capture_output else None
stderr = subprocess.STDOUT if capture_output else None
self.process = subprocess.Popen(self.cmd, stdout=stdout,
stderr=stderr, env=env)
def wait(self):
self.process.wait()
self.stdout_capturer.halt()
self.stdout = self.stdout_capturer.get_buffer()
return self.process.returncode
def print_extra_info(self):
"""Print extra information about this test run.
If we're running in parallel and showing the concise view, this is only
called if the test group fails. Otherwise, it's called before the test
group is started.
The base implementation does nothing, but it can be overridden by
subclasses.
"""
return
def cleanup_process(self):
"""Cleanup on exit by killing any leftover processes."""
subp = self.process
if subp is None or (subp.poll() is not None):
return # Process doesn't exist, or is already dead.
try:
print('Cleaning up stale PID: %d' % subp.pid)
subp.kill()
except: # (OSError, WindowsError) ?
# This is just a best effort, if we fail or the process was
# really gone, ignore it.
pass
else:
for i in range(10):
if subp.poll() is None:
time.sleep(0.1)
else:
break
if subp.poll() is None:
# The process did not die...
print('... failed. Manual cleanup may be required.')
def cleanup(self):
"Kill process if it's still alive, and clean up temporary directories"
self.cleanup_process()
for td in self.dirs:
td.cleanup()
__del__ = cleanup
def get_js_test_dir():
import notebook.tests as t
return os.path.join(os.path.dirname(t.__file__), '')
def all_js_groups():
import glob
test_dir = get_js_test_dir()
all_subdirs = glob.glob(test_dir + '[!_]*/')
return [os.path.relpath(x, test_dir) for x in all_subdirs]
class JSController(TestController):
"""Run CasperJS tests """
requirements = ['casperjs']
def __init__(self, section, xunit=True, engine='phantomjs', url=None):
"""Create new test runner."""
TestController.__init__(self)
self.engine = engine
self.section = section
self.xunit = xunit
self.url = url
# run with a base URL that would be escaped,
# to test that we don't double-escape URLs
self.base_url = '/a@b/'
self.slimer_failure = re.compile('^FAIL.*', flags=re.MULTILINE)
js_test_dir = get_js_test_dir()
includes = '--includes=' + os.path.join(js_test_dir,'util.js')
test_cases = os.path.join(js_test_dir, self.section)
self.cmd = ['casperjs', 'test', includes, test_cases, '--engine=%s' % self.engine]
def setup(self):
self.ipydir = TemporaryDirectory()
self.config_dir = TemporaryDirectory()
self.nbdir = TemporaryDirectory()
self.home = TemporaryDirectory()
self.env = {
'HOME': self.home.name,
'JUPYTER_CONFIG_DIR': self.config_dir.name,
'IPYTHONDIR': self.ipydir.name,
}
self.dirs.append(self.ipydir)
self.dirs.append(self.home)
self.dirs.append(self.config_dir)
self.dirs.append(self.nbdir)
os.makedirs(os.path.join(self.nbdir.name, os.path.join(u'sub ∂ir1', u'sub ∂ir 1a')))
os.makedirs(os.path.join(self.nbdir.name, os.path.join(u'sub ∂ir2', u'sub ∂ir 1b')))
if self.xunit:
self.add_xunit()
# If a url was specified, use that for the testing.
if self.url:
try:
alive = requests.get(self.url).status_code == 200
except:
alive = False
if alive:
self.cmd.append("--url=%s" % self.url)
else:
raise Exception('Could not reach "%s".' % self.url)
else:
# start the ipython notebook, so we get the port number
self.server_port = 0
self._init_server()
if self.server_port:
self.cmd.append('--url=http://localhost:%i%s' % (self.server_port, self.base_url))
else:
# don't launch tests if the server didn't start
self.cmd = [sys.executable, '-c', 'raise SystemExit(1)']
def add_xunit(self):
xunit_file = os.path.abspath(self.section.replace('/','.') + '.xunit.xml')
self.cmd.append('--xunit=%s' % xunit_file)
def launch(self, buffer_output):
# If the engine is SlimerJS, we need to buffer the output because
# SlimerJS does not support exit codes, so CasperJS always returns 0.
if self.engine == 'slimerjs' and not buffer_output:
return super().launch(capture_output=True)
else:
return super().launch(buffer_output=buffer_output)
def wait(self, *pargs, **kwargs):
"""Wait for the JSController to finish"""
ret = super().wait(*pargs, **kwargs)
# If this is a SlimerJS controller, check the captured stdout for
# errors. Otherwise, just return the return code.
if self.engine == 'slimerjs':
stdout = bytes_to_str(self.stdout)
if ret != 0:
# This could still happen e.g. if it's stopped by SIGINT
return ret
return bool(self.slimer_failure.search(stdout))
else:
return ret
def print_extra_info(self):
print("Running tests with notebook directory %r" % self.nbdir.name)
@property
def will_run(self):
should_run = all(have[a] for a in self.requirements + [self.engine])
return should_run
def _init_server(self):
"Start the notebook server in a separate process"
self.server_command = command = [sys.executable,
'-m', 'notebook',
'--no-browser',
'--notebook-dir', self.nbdir.name,
'--NotebookApp.token=',
'--NotebookApp.base_url=%s' % self.base_url,
]
# ipc doesn't work on Windows, and darwin has crazy-long temp paths,
# which run afoul of ipc's maximum path length.
if sys.platform.startswith('linux'):
command.append('--KernelManager.transport=ipc')
self.stream_capturer = c = StreamCapturer()
c.start()
env = os.environ.copy()
env.update(self.env)
self.server = subprocess.Popen(command,
stdout = c.writefd,
stderr = subprocess.STDOUT,
cwd=self.nbdir.name,
env=env,
)
with patch.dict('os.environ', {'HOME': self.home.name}):
runtime_dir = jupyter_runtime_dir()
self.server_info_file = os.path.join(runtime_dir,
'nbserver-%i.json' % self.server.pid
)
self._wait_for_server()
def _wait_for_server(self):
"""Wait 30 seconds for the notebook server to start"""
for i in range(300):
if self.server.poll() is not None:
return self._failed_to_start()
if os.path.exists(self.server_info_file):
try:
self._load_server_info()
except ValueError:
# If the server is halfway through writing the file, we may
# get invalid JSON; it should be ready next iteration.
pass
else:
return
time.sleep(0.1)
print("Notebook server-info file never arrived: %s" % self.server_info_file,
file=sys.stderr
)
def _failed_to_start(self):
"""Notebook server exited prematurely"""
captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
print("Notebook failed to start: ", file=sys.stderr)
print(self.server_command)
print(captured, file=sys.stderr)
def _load_server_info(self):
"""Notebook server started, load connection info from JSON"""
with open(self.server_info_file) as f:
info = json.load(f)
self.server_port = info['port']
def cleanup(self):
if hasattr(self, 'server'):
try:
self.server.terminate()
except OSError:
# already dead
pass
# wait 10s for the server to shutdown
try:
popen_wait(self.server, NOTEBOOK_SHUTDOWN_TIMEOUT)
except TimeoutExpired:
# server didn't terminate, kill it
try:
print("Failed to terminate notebook server, killing it.",
file=sys.stderr
)
self.server.kill()
except OSError:
# already dead
pass
# wait another 10s
try:
popen_wait(self.server, NOTEBOOK_SHUTDOWN_TIMEOUT)
except TimeoutExpired:
print("Notebook server still running (%s)" % self.server_info_file,
file=sys.stderr
)
self.stream_capturer.halt()
TestController.cleanup(self)
def prepare_controllers(options):
"""Returns two lists of TestController instances, those to run, and those
not to run."""
testgroups = options.testgroups
if not testgroups:
testgroups = all_js_groups()
engine = 'slimerjs' if options.slimerjs else 'phantomjs'
c_js = [JSController(name, xunit=options.xunit, engine=engine, url=options.url) for name in testgroups]
controllers = c_js
to_run = [c for c in controllers if c.will_run]
not_run = [c for c in controllers if not c.will_run]
return to_run, not_run
def do_run(controller, buffer_output=True):
"""Setup and run a test controller.
If buffer_output is True, no output is displayed, to avoid it appearing
interleaved. In this case, the caller is responsible for displaying test
output on failure.
Returns
-------
controller : TestController
The same controller as passed in, as a convenience for using map() type
APIs.
exitcode : int
The exit code of the test subprocess. Non-zero indicates failure.
"""
try:
try:
controller.setup()
if not buffer_output:
controller.print_extra_info()
controller.launch(buffer_output=buffer_output)
except Exception:
import traceback
traceback.print_exc()
return controller, 1 # signal failure
exitcode = controller.wait()
return controller, exitcode
except KeyboardInterrupt:
return controller, -signal.SIGINT
finally:
controller.cleanup()
def report():
"""Return a string with a summary report of test-related variables."""
inf = get_sys_info()
out = []
def _add(name, value):
out.append((name, value))
_add('Python version', inf['sys_version'].replace('\n',''))
_add('sys.executable', inf['sys_executable'])
_add('Platform', inf['platform'])
width = max(len(n) for (n,v) in out)
out = ["{:<{width}}: {}\n".format(n, v, width=width) for (n,v) in out]
avail = []
not_avail = []
for k, is_avail in have.items():
if is_avail:
avail.append(k)
else:
not_avail.append(k)
if avail:
out.append('\nTools and libraries available at test time:\n')
avail.sort()
out.append(' ' + ' '.join(avail)+'\n')
if not_avail:
out.append('\nTools and libraries NOT available at test time:\n')
not_avail.sort()
out.append(' ' + ' '.join(not_avail)+'\n')
return ''.join(out)
def run_jstestall(options):
"""Run the entire Javascript test suite.
This function constructs TestControllers and runs them in subprocesses.
Parameters
----------
All parameters are passed as attributes of the options object.
testgroups : list of str
Run only these sections of the test suite. If empty, run all the available
sections.
fast : int or None
Run the test suite in parallel, using n simultaneous processes. If None
is passed, one process is used per CPU core. Default 1 (i.e. sequential)
inc_slow : bool
Include slow tests. By default, these tests aren't run.
slimerjs : bool
Use slimerjs if it's installed instead of phantomjs for casperjs tests.
url : unicode
Address:port to use when running the JS tests.
xunit : bool
Produce Xunit XML output. This is written to multiple foo.xunit.xml files.
extra_args : list
Extra arguments to pass to the test subprocesses, e.g. '-v'
"""
to_run, not_run = prepare_controllers(options)
def justify(ltext, rtext, width=70, fill='-'):
ltext += ' '
rtext = (' ' + rtext).rjust(width - len(ltext), fill)
return ltext + rtext
# Run all test runners, tracking execution time
failed = []
t_start = time.time()
print()
if options.fast == 1:
# This actually means sequential, i.e. with 1 job
for controller in to_run:
print('Test group:', controller.section)
sys.stdout.flush() # Show in correct order when output is piped
controller, res = do_run(controller, buffer_output=False)
if res:
failed.append(controller)
if res == -signal.SIGINT:
print("Interrupted")
break
print()
else:
# Run tests concurrently
try:
pool = multiprocessing.pool.ThreadPool(options.fast)
for (controller, res) in pool.imap_unordered(do_run, to_run):
res_string = 'OK' if res == 0 else 'FAILED'
print(justify('Test group: ' + controller.section, res_string))
if res:
controller.print_extra_info()
print(bytes_to_str(controller.stdout))
failed.append(controller)
if res == -signal.SIGINT:
print("Interrupted")
break
except KeyboardInterrupt:
return
for controller in not_run:
print(justify('Test group: ' + controller.section, 'NOT RUN'))
t_end = time.time()
t_tests = t_end - t_start
nrunners = len(to_run)
nfail = len(failed)
# summarize results
print('_'*70)
print('Test suite completed for system with the following information:')
print(report())
took = "Took %.3fs." % t_tests
print('Status: ', end='')
if not failed:
print('OK (%d test groups).' % nrunners, took)
else:
# If anything went wrong, point out what command to rerun manually to
# see the actual errors and individual summary
failed_sections = [c.section for c in failed]
print('ERROR - {} out of {} test groups failed ({}).'.format(nfail,
nrunners, ', '.join(failed_sections)), took)
print()
print('You may wish to rerun these, with:')
print(' python -m notebook.jstest', *failed_sections)
print()
if failed:
# Ensure that our exit code indicates failure
sys.exit(1)
argparser = argparse.ArgumentParser(description='Run Jupyter Notebook Javascript tests')
argparser.add_argument('testgroups', nargs='*',
help='Run specified groups of tests. If omitted, run '
'all tests.')
argparser.add_argument('--slimerjs', action='store_true',
help="Use slimerjs if it's installed instead of phantomjs for casperjs tests.")
argparser.add_argument('--url', help="URL to use for the JS tests.")
argparser.add_argument('-j', '--fast', nargs='?', const=None, default=1, type=int,
help='Run test sections in parallel. This starts as many '
'processes as you have cores, or you can specify a number.')
argparser.add_argument('--xunit', action='store_true',
help='Produce Xunit XML results')
argparser.add_argument('--subproc-streams', default='capture',
help="What to do with stdout/stderr from subprocesses. "
"'capture' (default), 'show' and 'discard' are the options.")
def default_options():
"""Get an argparse Namespace object with the default arguments, to pass to
:func:`run_iptestall`.
"""
options = argparser.parse_args([])
options.extra_args = []
return options
def main():
try:
ix = sys.argv.index('--')
except ValueError:
to_parse = sys.argv[1:]
extra_args = []
else:
to_parse = sys.argv[1:ix]
extra_args = sys.argv[ix+1:]
options = argparser.parse_args(to_parse)
options.extra_args = extra_args
run_jstestall(options)
if __name__ == '__main__':
main()

@ -1,28 +0,0 @@
from tornado import web
from ..base.handlers import IPythonHandler
from ..services.kernelspecs.handlers import kernel_name_regex
class KernelSpecResourceHandler(web.StaticFileHandler, IPythonHandler):
SUPPORTED_METHODS = ('GET', 'HEAD')
def initialize(self):
web.StaticFileHandler.initialize(self, path='')
@web.authenticated
def get(self, kernel_name, path, include_body=True):
ksm = self.kernel_spec_manager
try:
self.root = ksm.get_kernel_spec(kernel_name).resource_dir
except KeyError as e:
raise web.HTTPError(404,
u'Kernel spec %s not found' % kernel_name) from e
self.log.debug("Serving kernel resource from: %s", self.root)
return web.StaticFileHandler.get(self, path, include_body=include_body)
@web.authenticated
def head(self, kernel_name, path):
return self.get(kernel_name, path, include_body=False)
default_handlers = [
(r"/kernelspecs/%s/(?P<path>.*)" % kernel_name_regex, KernelSpecResourceHandler),
]

@ -1,56 +0,0 @@
#-----------------------------------------------------------------------------
# Copyright (c) Jupyter Development Team
#
# Distributed under the terms of the BSD License. The full license is in
# the file LICENSE, distributed as part of this software.
#-----------------------------------------------------------------------------
import json
from tornado.log import access_log
from .prometheus.log_functions import prometheus_log_method
def log_request(handler, log=access_log, log_json=False):
"""log a bit more information about each request than tornado's default
- move static file get success to debug-level (reduces noise)
- get proxied IP instead of proxy IP
- log referer for redirect and failed requests
- log user-agent for failed requests
"""
status = handler.get_status()
request = handler.request
if status < 300 or status == 304:
# Successes (or 304 FOUND) are debug-level
log_method = log.debug
elif status < 400:
log_method = log.info
elif status < 500:
log_method = log.warning
else:
log_method = log.error
request_time = 1000.0 * request.request_time()
ns = dict(
status=status,
method=request.method,
ip=request.remote_ip,
uri=request.uri,
request_time=float('%.2f' % request_time),
)
msg = "{status} {method} {uri} ({ip}) {request_time:f}ms"
if status >= 400:
# log bad referers
ns['referer'] = request.headers.get('Referer', 'None')
msg = msg + ' referer={referer}'
if status >= 500 and status != 502:
# log all headers if it caused an error
if log_json:
log_method("", extra=dict(props=dict(request.headers)))
else:
log_method(json.dumps(dict(request.headers), indent=2))
if log_json:
log_method("", extra=dict(props=ns))
else:
log_method(msg.format(**ns))
prometheus_log_method(handler)

@ -1,200 +0,0 @@
"""Tornado handlers for nbconvert."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import io
import os
import zipfile
from tornado import gen, web, escape
from tornado.log import app_log
from ..base.handlers import (
IPythonHandler, FilesRedirectHandler,
path_regex,
)
from ..utils import maybe_future
from nbformat import from_dict
from ipython_genutils.py3compat import cast_bytes
from ipython_genutils import text
def find_resource_files(output_files_dir):
files = []
for dirpath, dirnames, filenames in os.walk(output_files_dir):
files.extend([os.path.join(dirpath, f) for f in filenames])
return files
def respond_zip(handler, name, output, resources):
"""Zip up the output and resource files and respond with the zip file.
Returns True if it has served a zip file, False if there are no resource
files, in which case we serve the plain output file.
"""
# Check if we have resource files we need to zip
output_files = resources.get('outputs', None)
if not output_files:
return False
# Headers
zip_filename = os.path.splitext(name)[0] + '.zip'
handler.set_attachment_header(zip_filename)
handler.set_header('Content-Type', 'application/zip')
handler.set_header('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
# Prepare the zip file
buffer = io.BytesIO()
zipf = zipfile.ZipFile(buffer, mode='w', compression=zipfile.ZIP_DEFLATED)
output_filename = os.path.splitext(name)[0] + resources['output_extension']
zipf.writestr(output_filename, cast_bytes(output, 'utf-8'))
for filename, data in output_files.items():
zipf.writestr(os.path.basename(filename), data)
zipf.close()
handler.finish(buffer.getvalue())
return True
def get_exporter(format, **kwargs):
"""get an exporter, raising appropriate errors"""
# if this fails, will raise 500
try:
from nbconvert.exporters.base import get_exporter
except ImportError as e:
raise web.HTTPError(500, "Could not import nbconvert: %s" % e) from e
try:
Exporter = get_exporter(format)
except KeyError as e:
# should this be 400?
raise web.HTTPError(404, u"No exporter for format: %s" % format) from e
try:
return Exporter(**kwargs)
except Exception as e:
app_log.exception("Could not construct Exporter: %s", Exporter)
raise web.HTTPError(500, "Could not construct Exporter: %s" % e) from e
class NbconvertFileHandler(IPythonHandler):
SUPPORTED_METHODS = ('GET',)
@property
def content_security_policy(self):
# In case we're serving HTML/SVG, confine any Javascript to a unique
# origin so it can't interact with the notebook server.
return super().content_security_policy + "; sandbox allow-scripts"
@web.authenticated
@gen.coroutine
def get(self, format, path):
exporter = get_exporter(format, config=self.config, log=self.log)
path = path.strip('/')
# If the notebook relates to a real file (default contents manager),
# give its path to nbconvert.
if hasattr(self.contents_manager, '_get_os_path'):
os_path = self.contents_manager._get_os_path(path)
ext_resources_dir, basename = os.path.split(os_path)
else:
ext_resources_dir = None
model = yield maybe_future(self.contents_manager.get(path=path))
name = model['name']
if model['type'] != 'notebook':
# not a notebook, redirect to files
return FilesRedirectHandler.redirect_to_files(self, path)
nb = model['content']
self.set_header('Last-Modified', model['last_modified'])
# create resources dictionary
mod_date = model['last_modified'].strftime(text.date_format)
nb_title = os.path.splitext(name)[0]
resource_dict = {
"metadata": {
"name": nb_title,
"modified_date": mod_date
},
"config_dir": self.application.settings['config_dir']
}
if ext_resources_dir:
resource_dict['metadata']['path'] = ext_resources_dir
try:
output, resources = exporter.from_notebook_node(
nb,
resources=resource_dict
)
except Exception as e:
self.log.exception("nbconvert failed: %s", e)
raise web.HTTPError(500, "nbconvert failed: %s" % e) from e
if respond_zip(self, name, output, resources):
return
# Force download if requested
if self.get_argument('download', 'false').lower() == 'true':
filename = os.path.splitext(name)[0] + resources['output_extension']
self.set_attachment_header(filename)
# MIME type
if exporter.output_mimetype:
self.set_header('Content-Type',
'%s; charset=utf-8' % exporter.output_mimetype)
self.set_header('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
self.finish(output)
class NbconvertPostHandler(IPythonHandler):
SUPPORTED_METHODS = ('POST',)
@property
def content_security_policy(self):
# In case we're serving HTML/SVG, confine any Javascript to a unique
# origin so it can't interact with the notebook server.
return super().content_security_policy + "; sandbox allow-scripts"
@web.authenticated
def post(self, format):
exporter = get_exporter(format, config=self.config)
model = self.get_json_body()
name = model.get('name', 'notebook.ipynb')
nbnode = from_dict(model['content'])
try:
output, resources = exporter.from_notebook_node(nbnode, resources={
"metadata": {"name": name[:name.rfind('.')],},
"config_dir": self.application.settings['config_dir'],
})
except Exception as e:
raise web.HTTPError(500, "nbconvert failed: %s" % e) from e
if respond_zip(self, name, output, resources):
return
# MIME type
if exporter.output_mimetype:
self.set_header('Content-Type',
'%s; charset=utf-8' % exporter.output_mimetype)
self.finish(output)
#-----------------------------------------------------------------------------
# URL to handler mappings
#-----------------------------------------------------------------------------
_format_regex = r"(?P<format>\w+)"
default_handlers = [
(r"/nbconvert/%s" % _format_regex, NbconvertPostHandler),
(r"/nbconvert/%s%s" % (_format_regex, path_regex),
NbconvertFileHandler),
]

@ -1,158 +0,0 @@
import io
import json
import os
from os.path import join as pjoin
import shutil
import requests
import pytest
from notebook.utils import url_path_join
from notebook.tests.launchnotebook import NotebookTestBase, assert_http_error
from nbformat import write
from nbformat.v4 import (
new_notebook, new_markdown_cell, new_code_cell, new_output,
)
from ipython_genutils.testing.decorators import onlyif_cmds_exist
from base64 import encodebytes
def cmd_exists(cmd):
"""Check is a command exists."""
if shutil.which(cmd) is None:
return False
return True
class NbconvertAPI(object):
"""Wrapper for nbconvert API calls."""
def __init__(self, request):
self.request = request
def _req(self, verb, path, body=None, params=None):
response = self.request(verb,
url_path_join('nbconvert', path),
data=body, params=params,
)
response.raise_for_status()
return response
def from_file(self, format, path, name, download=False):
return self._req('GET', url_path_join(format, path, name),
params={'download':download})
def from_post(self, format, nbmodel):
body = json.dumps(nbmodel)
return self._req('POST', format, body)
def list_formats(self):
return self._req('GET', '')
png_green_pixel = encodebytes(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00'
b'\x00\x00\x01\x00\x00x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\x0cIDAT'
b'\x08\xd7c\x90\xfb\xcf\x00\x00\x02\\\x01\x1e.~d\x87\x00\x00\x00\x00IEND\xaeB`\x82'
).decode('ascii')
class APITest(NotebookTestBase):
def setUp(self):
nbdir = self.notebook_dir
if not os.path.isdir(pjoin(nbdir, 'foo')):
subdir = pjoin(nbdir, 'foo')
os.mkdir(subdir)
# Make sure that we clean this up when we're done.
# By using addCleanup this will happen correctly even if we fail
# later in setUp.
@self.addCleanup
def cleanup_dir():
shutil.rmtree(subdir, ignore_errors=True)
nb = new_notebook()
nb.cells.append(new_markdown_cell(u'Created by test ³'))
cc1 = new_code_cell(source=u'print(2*6)')
cc1.outputs.append(new_output(output_type="stream", text=u'12'))
cc1.outputs.append(new_output(output_type="execute_result",
data={'image/png' : png_green_pixel},
execution_count=1,
))
nb.cells.append(cc1)
with io.open(pjoin(nbdir, 'foo', 'testnb.ipynb'), 'w',
encoding='utf-8') as f:
write(nb, f, version=4)
self.nbconvert_api = NbconvertAPI(self.request)
@pytest.mark.skipif(
not cmd_exists('pandoc'),
reason="Pandoc wasn't found. Skipping this test."
)
def test_from_file(self):
r = self.nbconvert_api.from_file('html', 'foo', 'testnb.ipynb')
self.assertEqual(r.status_code, 200)
self.assertIn(u'text/html', r.headers['Content-Type'])
self.assertIn(u'Created by test', r.text)
self.assertIn(u'print', r.text)
r = self.nbconvert_api.from_file('python', 'foo', 'testnb.ipynb')
self.assertIn(u'text/x-python', r.headers['Content-Type'])
self.assertIn(u'print(2*6)', r.text)
@pytest.mark.skipif(
not cmd_exists('pandoc'),
reason="Pandoc wasn't found. Skipping this test."
)
def test_from_file_404(self):
with assert_http_error(404):
self.nbconvert_api.from_file('html', 'foo', 'thisdoesntexist.ipynb')
@pytest.mark.skipif(
not cmd_exists('pandoc'),
reason="Pandoc wasn't found. Skipping this test."
)
def test_from_file_download(self):
r = self.nbconvert_api.from_file('python', 'foo', 'testnb.ipynb', download=True)
content_disposition = r.headers['Content-Disposition']
self.assertIn('attachment', content_disposition)
self.assertIn('testnb.py', content_disposition)
@pytest.mark.skipif(
not cmd_exists('pandoc'),
reason="Pandoc wasn't found. Skipping this test."
)
def test_from_file_zip(self):
r = self.nbconvert_api.from_file('latex', 'foo', 'testnb.ipynb', download=True)
self.assertIn(u'application/zip', r.headers['Content-Type'])
self.assertIn(u'.zip', r.headers['Content-Disposition'])
@pytest.mark.skipif(
not cmd_exists('pandoc'),
reason="Pandoc wasn't found. Skipping this test."
)
def test_from_post(self):
nbmodel = self.request('GET', 'api/contents/foo/testnb.ipynb').json()
r = self.nbconvert_api.from_post(format='html', nbmodel=nbmodel)
self.assertEqual(r.status_code, 200)
self.assertIn(u'text/html', r.headers['Content-Type'])
self.assertIn(u'Created by test', r.text)
self.assertIn(u'print', r.text)
r = self.nbconvert_api.from_post(format='python', nbmodel=nbmodel)
self.assertIn(u'text/x-python', r.headers['Content-Type'])
self.assertIn(u'print(2*6)', r.text)
@pytest.mark.skipif(
not cmd_exists('pandoc'),
reason="Pandoc wasn't found. Skipping this test."
)
def test_from_post_zip(self):
nbmodel = self.request('GET', 'api/contents/foo/testnb.ipynb').json()
r = self.nbconvert_api.from_post(format='latex', nbmodel=nbmodel)
self.assertIn(u'application/zip', r.headers['Content-Type'])
self.assertIn(u'.zip', r.headers['Content-Disposition'])

File diff suppressed because it is too large Load Diff

@ -1,114 +0,0 @@
"""Tornado handlers for the live notebook view."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from collections import namedtuple
import os
from tornado import (
gen, web,
)
HTTPError = web.HTTPError
from ..base.handlers import (
IPythonHandler, FilesRedirectHandler, path_regex,
)
from ..utils import (
maybe_future, url_escape,
)
from ..transutils import _
def get_frontend_exporters():
from nbconvert.exporters.base import get_export_names, get_exporter
# name=exporter_name, display=export_from_notebook+extension
ExporterInfo = namedtuple('ExporterInfo', ['name', 'display'])
default_exporters = [
ExporterInfo(name='html', display='HTML (.html)'),
ExporterInfo(name='latex', display='LaTeX (.tex)'),
ExporterInfo(name='markdown', display='Markdown (.md)'),
ExporterInfo(name='notebook', display='Notebook (.ipynb)'),
ExporterInfo(name='pdf', display='PDF via LaTeX (.pdf)'),
ExporterInfo(name='rst', display='reST (.rst)'),
ExporterInfo(name='script', display='Script (.txt)'),
ExporterInfo(name='slides', display='Reveal.js slides (.slides.html)')
]
frontend_exporters = []
for name in get_export_names():
exporter_class = get_exporter(name)
exporter_instance = exporter_class()
ux_name = getattr(exporter_instance, 'export_from_notebook', None)
super_uxname = getattr(super(exporter_class, exporter_instance),
'export_from_notebook', None)
# Ensure export_from_notebook is explicitly defined & not inherited
if ux_name is not None and ux_name != super_uxname:
display = _('{} ({})'.format(ux_name,
exporter_instance.file_extension))
frontend_exporters.append(ExporterInfo(name, display))
# Ensure default_exporters are in frontend_exporters if not already
# This protects against nbconvert versions lower than 5.5
names = set(exporter.name.lower() for exporter in frontend_exporters)
for exporter in default_exporters:
if exporter.name not in names:
frontend_exporters.append(exporter)
# Protect against nbconvert 5.5.0
python_exporter = ExporterInfo(name='python', display='python (.py)')
if python_exporter in frontend_exporters:
frontend_exporters.remove(python_exporter)
# Protect against nbconvert 5.4.x
template_exporter = ExporterInfo(name='custom', display='custom (.txt)')
if template_exporter in frontend_exporters:
frontend_exporters.remove(template_exporter)
return sorted(frontend_exporters)
class NotebookHandler(IPythonHandler):
@web.authenticated
@gen.coroutine
def get(self, path):
"""get renders the notebook template if a name is given, or
redirects to the '/files/' handler if the name is not given."""
path = path.strip('/')
cm = self.contents_manager
# will raise 404 on not found
try:
model = yield maybe_future(cm.get(path, content=False))
except web.HTTPError as e:
if e.status_code == 404 and 'files' in path.split('/'):
# 404, but '/files/' in URL, let FilesRedirect take care of it
return FilesRedirectHandler.redirect_to_files(self, path)
else:
raise
if model['type'] != 'notebook':
# not a notebook, redirect to files
return FilesRedirectHandler.redirect_to_files(self, path)
name = path.rsplit('/', 1)[-1]
self.write(self.render_template('notebook.html',
notebook_path=path,
notebook_name=name,
kill_kernel=False,
mathjax_url=self.mathjax_url,
mathjax_config=self.mathjax_config,
get_frontend_exporters=get_frontend_exporters
)
)
#-----------------------------------------------------------------------------
# URL to handler mappings
#-----------------------------------------------------------------------------
default_handlers = [
(r"/notebooks%s" % path_regex, NotebookHandler),
]

File diff suppressed because it is too large Load Diff

@ -1,4 +0,0 @@
"""
A package containing all the functionality and
configuration connected to the prometheus metrics
"""

@ -1,24 +0,0 @@
from ..prometheus.metrics import HTTP_REQUEST_DURATION_SECONDS
def prometheus_log_method(handler):
"""
Tornado log handler for recording RED metrics.
We record the following metrics:
Rate - the number of requests, per second, your services are serving.
Errors - the number of failed requests per second.
Duration - The amount of time each request takes expressed as a time interval.
We use a fully qualified name of the handler as a label,
rather than every url path to reduce cardinality.
This function should be either the value of or called from a function
that is the 'log_function' tornado setting. This makes it get called
at the end of every request, allowing us to record the metrics we need.
"""
HTTP_REQUEST_DURATION_SECONDS.labels(
method=handler.request.method,
handler='{}.{}'.format(handler.__class__.__module__, type(handler).__name__),
status_code=handler.get_status()
).observe(handler.request.request_time())

@ -1,27 +0,0 @@
"""
Prometheus metrics exported by Jupyter Notebook Server
Read https://prometheus.io/docs/practices/naming/ for naming
conventions for metrics & labels.
"""
from prometheus_client import Histogram, Gauge
HTTP_REQUEST_DURATION_SECONDS = Histogram(
'http_request_duration_seconds',
'duration in seconds for all HTTP requests',
['method', 'handler', 'status_code'],
)
TERMINAL_CURRENTLY_RUNNING_TOTAL = Gauge(
'terminal_currently_running_total',
'counter for how many terminals are running',
)
KERNEL_CURRENTLY_RUNNING_TOTAL = Gauge(
'kernel_currently_running_total',
'counter for how many kernels are running labeled by type',
['type']
)

@ -1,331 +0,0 @@
"""Utilities for installing server extensions for the notebook"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import importlib
import sys
from jupyter_core.paths import jupyter_config_path
from ._version import __version__
from .config_manager import BaseJSONConfigManager
from .extensions import (
BaseExtensionApp, _get_config_dir, GREEN_ENABLED, RED_DISABLED, GREEN_OK, RED_X
)
from traitlets import Bool
from traitlets.utils.importstring import import_item
# ------------------------------------------------------------------------------
# Public API
# ------------------------------------------------------------------------------
def toggle_serverextension_python(import_name, enabled=None, parent=None,
user=True, sys_prefix=False, logger=None):
"""Toggle a server extension.
By default, toggles the extension in the system-wide Jupyter configuration
location (e.g. /usr/local/etc/jupyter).
Parameters
----------
import_name : str
Importable Python module (dotted-notation) exposing the magic-named
`load_jupyter_server_extension` function
enabled : bool [default: None]
Toggle state for the extension. Set to None to toggle, True to enable,
and False to disable the extension.
parent : Configurable [default: None]
user : bool [default: True]
Toggle in the user's configuration location (e.g. ~/.jupyter).
sys_prefix : bool [default: False]
Toggle in the current Python environment's configuration location
(e.g. ~/.envs/my-env/etc/jupyter). Will override `user`.
logger : Jupyter logger [optional]
Logger instance to use
"""
user = False if sys_prefix else user
config_dir = _get_config_dir(user=user, sys_prefix=sys_prefix)
cm = BaseJSONConfigManager(parent=parent, config_dir=config_dir)
cfg = cm.get("jupyter_notebook_config")
server_extensions = (
cfg.setdefault("NotebookApp", {})
.setdefault("nbserver_extensions", {})
)
old_enabled = server_extensions.get(import_name, None)
new_enabled = enabled if enabled is not None else not old_enabled
if logger:
if new_enabled:
logger.info(u"Enabling: %s" % (import_name))
else:
logger.info(u"Disabling: %s" % (import_name))
server_extensions[import_name] = new_enabled
if logger:
logger.info(u"- Writing config: {}".format(config_dir))
cm.update("jupyter_notebook_config", cfg)
if new_enabled:
validate_serverextension(import_name, logger)
def validate_serverextension(import_name, logger=None):
"""Assess the health of an installed server extension
Returns a list of validation warnings.
Parameters
----------
import_name : str
Importable Python module (dotted-notation) exposing the magic-named
`load_jupyter_server_extension` function
logger : Jupyter logger [optional]
Logger instance to use
"""
warnings = []
infos = []
func = None
if logger:
logger.info(" - Validating...")
try:
mod = importlib.import_module(import_name)
func = getattr(mod, 'load_jupyter_server_extension', None)
version = getattr(mod, '__version__', '')
except Exception:
logger.warning("Error loading server extension %s", import_name)
import_msg = u" {} is {} importable?"
if func is not None:
infos.append(import_msg.format(GREEN_OK, import_name))
else:
warnings.append(import_msg.format(RED_X, import_name))
post_mortem = u" {} {} {}"
if logger:
if warnings:
[logger.info(info) for info in infos]
[logger.warn(warning) for warning in warnings]
else:
logger.info(post_mortem.format(import_name, version, GREEN_OK))
return warnings
# ----------------------------------------------------------------------
# Applications
# ----------------------------------------------------------------------
flags = {}
flags.update(BaseExtensionApp.flags)
flags.pop("y", None)
flags.pop("generate-config", None)
flags.update({
"user" : ({
"ToggleServerExtensionApp" : {
"user" : True,
}}, "Perform the operation for the current user"
),
"system" : ({
"ToggleServerExtensionApp" : {
"user" : False,
"sys_prefix": False,
}}, "Perform the operation system-wide"
),
"sys-prefix" : ({
"ToggleServerExtensionApp" : {
"sys_prefix" : True,
}}, "Use sys.prefix as the prefix for installing server extensions"
),
"py" : ({
"ToggleServerExtensionApp" : {
"python" : True,
}}, "Install from a Python package"
),
})
flags['python'] = flags['py']
class ToggleServerExtensionApp(BaseExtensionApp):
"""A base class for enabling/disabling extensions"""
name = "jupyter serverextension enable/disable"
description = "Enable/disable a server extension using frontend configuration files."
flags = flags
user = Bool(True, config=True, help="Whether to do a user install")
sys_prefix = Bool(False, config=True, help="Use the sys.prefix as the prefix")
python = Bool(False, config=True, help="Install from a Python package")
def toggle_server_extension(self, import_name):
"""Change the status of a named server extension.
Uses the value of `self._toggle_value`.
Parameters
---------
import_name : str
Importable Python module (dotted-notation) exposing the magic-named
`load_jupyter_server_extension` function
"""
toggle_serverextension_python(
import_name, self._toggle_value, parent=self, user=self.user,
sys_prefix=self.sys_prefix, logger=self.log)
def toggle_server_extension_python(self, package):
"""Change the status of some server extensions in a Python package.
Uses the value of `self._toggle_value`.
Parameters
---------
package : str
Importable Python module exposing the
magic-named `_jupyter_server_extension_paths` function
"""
m, server_exts = _get_server_extension_metadata(package)
for server_ext in server_exts:
module = server_ext['module']
self.toggle_server_extension(module)
def start(self):
"""Perform the App's actions as configured"""
if not self.extra_args:
sys.exit('Please specify a server extension/package to enable or disable')
for arg in self.extra_args:
if self.python:
self.toggle_server_extension_python(arg)
else:
self.toggle_server_extension(arg)
class EnableServerExtensionApp(ToggleServerExtensionApp):
"""An App that enables (and validates) Server Extensions"""
name = "jupyter serverextension enable"
description = """
Enable a serverextension in configuration.
Usage
jupyter serverextension enable [--system|--sys-prefix]
"""
_toggle_value = True
class DisableServerExtensionApp(ToggleServerExtensionApp):
"""An App that disables Server Extensions"""
name = "jupyter serverextension disable"
description = """
Disable a serverextension in configuration.
Usage
jupyter serverextension disable [--system|--sys-prefix]
"""
_toggle_value = False
class ListServerExtensionsApp(BaseExtensionApp):
"""An App that lists (and validates) Server Extensions"""
name = "jupyter serverextension list"
version = __version__
description = "List all server extensions known by the configuration system"
def list_server_extensions(self):
"""List all enabled and disabled server extensions, by config path
Enabled extensions are validated, potentially generating warnings.
"""
config_dirs = jupyter_config_path()
for config_dir in config_dirs:
cm = BaseJSONConfigManager(parent=self, config_dir=config_dir)
data = cm.get("jupyter_notebook_config")
server_extensions = (
data.setdefault("NotebookApp", {})
.setdefault("nbserver_extensions", {})
)
if server_extensions:
print(u'config dir: {}'.format(config_dir))
for import_name, enabled in server_extensions.items():
print(u' {} {}'.format(
import_name,
GREEN_ENABLED if enabled else RED_DISABLED))
validate_serverextension(import_name, self.log)
def start(self):
"""Perform the App's actions as configured"""
self.list_server_extensions()
_examples = """
jupyter serverextension list # list all configured server extensions
jupyter serverextension enable --py <packagename> # enable all server extensions in a Python package
jupyter serverextension disable --py <packagename> # disable all server extensions in a Python package
"""
class ServerExtensionApp(BaseExtensionApp):
"""Root level server extension app"""
name = "jupyter serverextension"
version = __version__
description = "Work with Jupyter server extensions"
examples = _examples
subcommands = dict(
enable=(EnableServerExtensionApp, "Enable a server extension"),
disable=(DisableServerExtensionApp, "Disable a server extension"),
list=(ListServerExtensionsApp, "List server extensions")
)
def start(self):
"""Perform the App's actions as configured"""
super().start()
# The above should have called a subcommand and raised NoStart; if we
# get here, it didn't, so we should self.log.info a message.
subcmds = ", ".join(sorted(self.subcommands))
sys.exit("Please supply at least one subcommand: %s" % subcmds)
main = ServerExtensionApp.launch_instance
# ------------------------------------------------------------------------------
# Private API
# ------------------------------------------------------------------------------
def _get_server_extension_metadata(module):
"""Load server extension metadata from a module.
Returns a tuple of (
the package as loaded
a list of server extension specs: [
{
"module": "mockextension"
}
]
)
Parameters
----------
module : str
Importable Python module exposing the
magic-named `_jupyter_server_extension_paths` function
"""
m = import_item(module)
if not hasattr(m, '_jupyter_server_extension_paths'):
raise KeyError(u'The Python module {} does not include any valid server extensions'.format(module))
return m, m._jupyter_server_extension_paths()
if __name__ == '__main__':
main()

@ -1,857 +0,0 @@
swagger: '2.0'
info:
title: Jupyter Notebook API
description: Notebook API
version: "5"
contact:
name: Jupyter Project
url: https://jupyter.org
# will be prefixed to all paths
basePath: /
produces:
- application/json
consumes:
- application/json
parameters:
kernel:
name: kernel_id
required: true
in: path
description: kernel uuid
type: string
format: uuid
session:
name: session
required: true
in: path
description: session uuid
type: string
format: uuid
path:
name: path
required: true
in: path
description: file path
type: string
checkpoint_id:
name: checkpoint_id
required: true
in: path
description: Checkpoint id for a file
type: string
section_name:
name: section_name
required: true
in: path
description: Name of config section
type: string
terminal_id:
name: terminal_id
required: true
in: path
description: ID of terminal session
type: string
paths:
/api/contents/{path}:
parameters:
- $ref: '#/parameters/path'
get:
summary: Get contents of file or directory
description: "A client can optionally specify a type and/or format argument via URL parameter. When given, the Contents service shall return a model in the requested type and/or format. If the request cannot be satisfied, e.g. type=text is requested, but the file is binary, then the request shall fail with 400 and have a JSON response containing a 'reason' field, with the value 'bad format' or 'bad type', depending on what was requested."
tags:
- contents
parameters:
- name: type
in: query
description: File type ('file', 'directory')
type: string
enum:
- file
- directory
- name: format
in: query
description: "How file content should be returned ('text', 'base64')"
type: string
enum:
- text
- base64
- name: content
in: query
description: "Return content (0 for no content, 1 for return content)"
type: integer
responses:
404:
description: No item found
400:
description: Bad request
schema:
type: object
properties:
error:
type: string
description: Error condition
reason:
type: string
description: Explanation of error reason
200:
description: Contents of file or directory
headers:
Last-Modified:
description: Last modified date for file
type: string
format: dateTime
schema:
$ref: '#/definitions/Contents'
500:
description: Model key error
post:
summary: Create a new file in the specified path
description: "A POST to /api/contents/path creates a New untitled, empty file or directory. A POST to /api/contents/path with body {'copy_from': '/path/to/OtherNotebook.ipynb'} creates a new copy of OtherNotebook in path."
tags:
- contents
parameters:
- name: model
in: body
description: Path of file to copy
schema:
type: object
properties:
copy_from:
type: string
ext:
type: string
type:
type: string
responses:
201:
description: File created
headers:
Location:
description: URL for the new file
type: string
format: url
schema:
$ref: '#/definitions/Contents'
404:
description: No item found
400:
description: Bad request
schema:
type: object
properties:
error:
type: string
description: Error condition
reason:
type: string
description: Explanation of error reason
patch:
summary: Rename a file or directory without re-uploading content
tags:
- contents
parameters:
- name: path
in: body
required: true
description: New path for file or directory.
schema:
type: object
properties:
path:
type: string
format: path
description: New path for file or directory
responses:
200:
description: Path updated
headers:
Location:
description: Updated URL for the file or directory
type: string
format: url
schema:
$ref: '#/definitions/Contents'
400:
description: No data provided
schema:
type: object
properties:
error:
type: string
description: Error condition
reason:
type: string
description: Explanation of error reason
put:
summary: Save or upload file.
description: "Saves the file in the location specified by name and path. PUT is very similar to POST, but the requester specifies the name, whereas with POST, the server picks the name."
tags:
- contents
parameters:
- name: model
in: body
description: New path for file or directory
schema:
type: object
properties:
name:
type: string
description: The new filename if changed
path:
type: string
description: New path for file or directory
type:
type: string
description: Path dtype ('notebook', 'file', 'directory')
format:
type: string
description: File format ('json', 'text', 'base64')
content:
type: string
description: The actual body of the document excluding directory type
responses:
200:
description: File saved
headers:
Location:
description: Updated URL for the file or directory
type: string
format: url
schema:
$ref: '#/definitions/Contents'
201:
description: Path created
headers:
Location:
description: URL for the file or directory
type: string
format: url
schema:
$ref: '#/definitions/Contents'
400:
description: No data provided
schema:
type: object
properties:
error:
type: string
description: Error condition
reason:
type: string
description: Explanation of error reason
delete:
summary: Delete a file in the given path
tags:
- contents
responses:
204:
description: File deleted
headers:
Location:
description: URL for the removed file
type: string
format: url
/api/contents/{path}/checkpoints:
parameters:
- $ref: '#/parameters/path'
get:
summary: Get a list of checkpoints for a file
description: List checkpoints for a given file. There will typically be zero or one results.
tags:
- contents
responses:
404:
description: No item found
400:
description: Bad request
schema:
type: object
properties:
error:
type: string
description: Error condition
reason:
type: string
description: Explanation of error reason
200:
description: List of checkpoints for a file
schema:
type: array
items:
$ref: '#/definitions/Checkpoints'
500:
description: Model key error
post:
summary: Create a new checkpoint for a file
description: "Create a new checkpoint with the current state of a file. With the default FileContentsManager, only one checkpoint is supported, so creating new checkpoints clobbers existing ones."
tags:
- contents
responses:
201:
description: Checkpoint created
headers:
Location:
description: URL for the checkpoint
type: string
format: url
schema:
$ref: '#/definitions/Checkpoints'
404:
description: No item found
400:
description: Bad request
schema:
type: object
properties:
error:
type: string
description: Error condition
reason:
type: string
description: Explanation of error reason
/api/contents/{path}/checkpoints/{checkpoint_id}:
post:
summary: Restore a file to a particular checkpointed state
parameters:
- $ref: "#/parameters/path"
- $ref: "#/parameters/checkpoint_id"
tags:
- contents
responses:
204:
description: Checkpoint restored
400:
description: Bad request
schema:
type: object
properties:
error:
type: string
description: Error condition
reason:
type: string
description: Explanation of error reason
delete:
summary: Delete a checkpoint
parameters:
- $ref: "#/parameters/path"
- $ref: "#/parameters/checkpoint_id"
tags:
- contents
responses:
204:
description: Checkpoint deleted
/api/sessions/{session}:
parameters:
- $ref: '#/parameters/session'
get:
summary: Get session
tags:
- sessions
responses:
200:
description: Session
schema:
$ref: '#/definitions/Session'
patch:
summary: "This can be used to rename the session."
tags:
- sessions
parameters:
- name: model
in: body
required: true
schema:
$ref: '#/definitions/Session'
responses:
200:
description: Session
schema:
$ref: '#/definitions/Session'
400:
description: No data provided
delete:
summary: Delete a session
tags:
- sessions
responses:
204:
description: Session (and kernel) were deleted
410:
description: "Kernel was deleted before the session, and the session was *not* deleted (TODO - check to make sure session wasn't deleted)"
/api/sessions:
get:
summary: List available sessions
tags:
- sessions
responses:
200:
description: List of current sessions
schema:
type: array
items:
$ref: '#/definitions/Session'
post:
summary: "Create a new session, or return an existing session if a session of the same name already exists"
tags:
- sessions
parameters:
- name: session
in: body
schema:
$ref: '#/definitions/Session'
responses:
201:
description: Session created or returned
schema:
$ref: '#/definitions/Session'
headers:
Location:
description: URL for session commands
type: string
format: url
501:
description: Session not available
schema:
type: object
description: error message
properties:
message:
type: string
short_message:
type: string
/api/kernels:
get:
summary: List the JSON data for all kernels that are currently running
tags:
- kernels
responses:
200:
description: List of currently-running kernel uuids
schema:
type: array
items:
$ref: '#/definitions/Kernel'
post:
summary: Start a kernel and return the uuid
tags:
- kernels
parameters:
- name: name
in: body
description: Kernel spec name (defaults to default kernel spec for server)
schema:
type: object
properties:
name:
type: string
responses:
201:
description: Kernel started
schema:
$ref: '#/definitions/Kernel'
headers:
Location:
description: Model for started kernel
type: string
format: url
/api/kernels/{kernel_id}:
parameters:
- $ref: '#/parameters/kernel'
get:
summary: Get kernel information
tags:
- kernels
responses:
200:
description: Kernel information
schema:
$ref: '#/definitions/Kernel'
delete:
summary: Kill a kernel and delete the kernel id
tags:
- kernels
responses:
204:
description: Kernel deleted
/api/kernels/{kernel_id}/interrupt:
parameters:
- $ref: '#/parameters/kernel'
post:
summary: Interrupt a kernel
tags:
- kernels
responses:
204:
description: Kernel interrupted
/api/kernels/{kernel_id}/restart:
parameters:
- $ref: '#/parameters/kernel'
post:
summary: Restart a kernel
tags:
- kernels
responses:
200:
description: Kernel interrupted
headers:
Location:
description: URL for kernel commands
type: string
format: url
schema:
$ref: '#/definitions/Kernel'
/api/kernelspecs:
get:
summary: Get kernel specs
tags:
- kernelspecs
responses:
200:
description: Kernel specs
schema:
type: object
properties:
default:
type: string
description: Default kernel name
kernelspecs:
type: object
additionalProperties:
$ref: '#/definitions/KernelSpec'
/api/config/{section_name}:
get:
summary: Get a configuration section by name
parameters:
- $ref: "#/parameters/section_name"
tags:
- config
responses:
200:
description: Configuration object
schema:
type: object
patch:
summary: Update a configuration section by name
tags:
- config
parameters:
- $ref: "#/parameters/section_name"
- name: configuration
in: body
schema:
type: object
responses:
200:
description: Configuration object
schema:
type: object
/api/terminals:
get:
summary: Get available terminals
tags:
- terminals
responses:
200:
description: A list of all available terminal ids.
schema:
type: array
items:
$ref: '#/definitions/Terminal'
403:
description: Forbidden to access
404:
description: Not found
post:
summary: Create a new terminal
tags:
- terminals
responses:
200:
description: Succesfully created a new terminal
schema:
$ref: '#/definitions/Terminal'
403:
description: Forbidden to access
404:
description: Not found
/api/terminals/{terminal_id}:
get:
summary: Get a terminal session corresponding to an id.
tags:
- terminals
parameters:
- $ref: '#/parameters/terminal_id'
responses:
200:
description: Terminal session with given id
schema:
$ref: '#/definitions/Terminal'
403:
description: Forbidden to access
404:
description: Not found
delete:
summary: Delete a terminal session corresponding to an id.
tags:
- terminals
parameters:
- $ref: '#/parameters/terminal_id'
responses:
204:
description: Succesfully deleted terminal session
403:
description: Forbidden to access
404:
description: Not found
/api/status:
get:
summary: Get the current status/activity of the server.
tags:
- status
responses:
200:
description: The current status of the server
schema:
$ref: '#/definitions/APIStatus'
/api/spec.yaml:
get:
summary: Get the current spec for the notebook server's APIs.
tags:
- api-spec
produces:
- text/x-yaml
responses:
200:
description: The current spec for the notebook server's APIs.
schema:
type: file
definitions:
APIStatus:
description: |
Notebook server API status.
Added in notebook 5.0.
properties:
started:
type: string
description: |
ISO8601 timestamp indicating when the notebook server started.
last_activity:
type: string
description: |
ISO8601 timestamp indicating the last activity on the server,
either on the REST API or kernel activity.
connections:
type: number
description: |
The total number of currently open connections to kernels.
kernels:
type: number
description: |
The total number of running kernels.
KernelSpec:
description: Kernel spec (contents of kernel.json)
properties:
name:
type: string
description: Unique name for kernel
KernelSpecFile:
$ref: '#/definitions/KernelSpecFile'
resources:
type: object
properties:
kernel.js:
type: string
format: filename
description: path for kernel.js file
kernel.css:
type: string
format: filename
description: path for kernel.css file
logo-*:
type: string
format: filename
description: path for logo file. Logo filenames are of the form `logo-widthxheight`
KernelSpecFile:
description: Kernel spec json file
required:
- argv
- display_name
- language
properties:
language:
type: string
description: The programming language which this kernel runs. This will be stored in notebook metadata.
argv:
type: array
description: "A list of command line arguments used to start the kernel. The text `{connection_file}` in any argument will be replaced with the path to the connection file."
items:
type: string
display_name:
type: string
description: "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."
codemirror_mode:
type: string
description: Codemirror mode. Can be a string *or* an valid Codemirror mode object. This defaults to the string from the `language` property.
env:
type: object
description: A dictionary of environment variables to set for the kernel. These will be added to the current environment variables.
additionalProperties:
type: string
help_links:
type: array
description: Help items to be displayed in the help menu in the notebook UI.
items:
type: object
required:
- text
- url
properties:
text:
type: string
description: menu item link text
url:
type: string
format: URL
description: menu item link url
Kernel:
description: Kernel information
required:
- id
- name
properties:
id:
type: string
format: uuid
description: uuid of kernel
name:
type: string
description: kernel spec name
last_activity:
type: string
description: |
ISO 8601 timestamp for the last-seen activity on this kernel.
Use this in combination with execution_state == 'idle' to identify
which kernels have been idle since a given time.
Timestamps will be UTC, indicated 'Z' suffix.
Added in notebook server 5.0.
connections:
type: number
description: |
The number of active connections to this kernel.
execution_state:
type: string
description: |
Current execution state of the kernel (typically 'idle' or 'busy', but may be other values, such as 'starting').
Added in notebook server 5.0.
Session:
description: A session
type: object
properties:
id:
type: string
format: uuid
path:
type: string
description: path to the session
name:
type: string
description: name of the session
type:
type: string
description: session type
kernel:
$ref: '#/definitions/Kernel'
Contents:
description: "A contents object. The content and format keys may be null if content is not contained. If type is 'file', then the mimetype will be null."
type: object
required:
- type
- name
- path
- writable
- created
- last_modified
- mimetype
- format
- content
properties:
name:
type: string
description: "Name of file or directory, equivalent to the last part of the path"
path:
type: string
description: Full path for file or directory
type:
type: string
description: Type of content
enum:
- directory
- file
- notebook
writable:
type: boolean
description: indicates whether the requester has permission to edit the file
created:
type: string
description: Creation timestamp
format: dateTime
last_modified:
type: string
description: Last modified timestamp
format: dateTime
size:
type: integer
description: "The size of the file or notebook in bytes. If no size is provided, defaults to null."
mimetype:
type: string
description: "The mimetype of a file. If content is not null, and type is 'file', this will contain the mimetype of the file, otherwise this will be null."
content:
type: string
description: "The content, if requested (otherwise null). Will be an array if type is 'directory'"
format:
type: string
description: Format of content (one of null, 'text', 'base64', 'json')
Checkpoints:
description: A checkpoint object.
type: object
required:
- id
- last_modified
properties:
id:
type: string
description: Unique id for the checkpoint.
last_modified:
type: string
description: Last modified timestamp
format: dateTime
Terminal:
description: A Terminal object
type: object
required:
- name
properties:
name:
type: string
description: name of terminal
last_activity:
type: string
description: |
ISO 8601 timestamp for the last-seen activity on this terminal. Use
this to identify which terminals have been inactive since a given time.
Timestamps will be UTC, indicated 'Z' suffix.

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save