Skip to content

Commit

Permalink
Merge pull request #7278 from minrk/nb-file-ext
Browse files Browse the repository at this point in the history
don't enforce .ipynb extension in URLs
  • Loading branch information
Carreau committed Jan 6, 2015
2 parents 8c4d80a + 5bcd54d commit 3c0bbde
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 37 deletions.
12 changes: 10 additions & 2 deletions IPython/html/base/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,13 @@ def get(self):

class FilesRedirectHandler(IPythonHandler):
"""Handler for redirecting relative URLs to the /files/ handler"""
def get(self, path=''):

@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
Expand All @@ -488,6 +494,9 @@ def get(self, path=''):
url = url_escape(url)
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)


#-----------------------------------------------------------------------------
Expand All @@ -496,7 +505,6 @@ def get(self, path=''):

# path matches any number of `/foo[/bar...]` or just `/` or ''
path_regex = r"(?P<path>(?:(?:/[^/]+)+|/?))"
notebook_path_regex = r"(?P<path>(?:/[^/]+)+\.ipynb)"

#-----------------------------------------------------------------------------
# URL to handler mappings
Expand Down
8 changes: 5 additions & 3 deletions IPython/html/nbconvert/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from ..base.handlers import (
IPythonHandler, FilesRedirectHandler,
notebook_path_regex, path_regex,
path_regex,
)
from IPython.nbformat import from_dict

Expand Down Expand Up @@ -83,6 +83,8 @@ def get(self, format, path):
path = path.strip('/')
model = self.contents_manager.get(path=path)
name = model['name']
if model['type'] != 'notebook':
raise web.HTTPError(400, "Not a notebook: %s" % path)

self.set_header('Last-Modified', model['last_modified'])

Expand Down Expand Up @@ -142,8 +144,8 @@ def post(self, format):


default_handlers = [
(r"/nbconvert/%s%s" % (_format_regex, notebook_path_regex),
NbconvertFileHandler),
(r"/nbconvert/%s" % _format_regex, NbconvertPostHandler),
(r"/nbconvert/%s%s" % (_format_regex, path_regex),
NbconvertFileHandler),
(r"/nbconvert/html%s" % path_regex, FilesRedirectHandler),
]
21 changes: 14 additions & 7 deletions IPython/html/notebook/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
HTTPError = web.HTTPError

from ..base.handlers import (
IPythonHandler, FilesRedirectHandler,
notebook_path_regex, path_regex,
IPythonHandler, FilesRedirectHandler, path_regex,
)
from ..utils import url_escape

Expand All @@ -23,9 +22,18 @@ def get(self, path):
path = path.strip('/')
cm = self.contents_manager

# a .ipynb filename was given
if not cm.file_exists(path):
raise web.HTTPError(404, u'Notebook does not exist: %s' % path)
# will raise 404 on not found
try:
model = 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 = url_escape(path.rsplit('/', 1)[-1])
path = url_escape(path)
self.write(self.render_template('notebook.html',
Expand All @@ -43,7 +51,6 @@ def get(self, path):


default_handlers = [
(r"/notebooks%s" % notebook_path_regex, NotebookHandler),
(r"/notebooks%s" % path_regex, FilesRedirectHandler),
(r"/notebooks%s" % path_regex, NotebookHandler),
]

15 changes: 12 additions & 3 deletions IPython/html/static/notebook/js/notebook.js
Original file line number Diff line number Diff line change
Expand Up @@ -2031,16 +2031,25 @@ define([
}
);
};

/**
* Ensure a filename has the right extension
* Returns the filename with the appropriate extension, appending if necessary.
*/
Notebook.prototype.ensure_extension = function (name) {
if (!name.match(/\.ipynb$/)) {
name = name + ".ipynb";
}
return name;
};

/**
* Rename the notebook.
* @param {string} new_name
* @return {Promise} promise that resolves when the notebook is renamed.
*/
Notebook.prototype.rename = function (new_name) {
if (!new_name.match(/\.ipynb$/)) {
new_name = new_name + ".ipynb";
}
new_name = this.ensure_extension(new_name);

var that = this;
var parent = utils.url_path_split(this.notebook_path)[0];
Expand Down
23 changes: 1 addition & 22 deletions IPython/html/tests/test_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import re
import nose.tools as nt

from IPython.html.base.handlers import path_regex, notebook_path_regex
from IPython.html.base.handlers import path_regex

try: # py3
assert_regex = nt.assert_regex
Expand All @@ -14,7 +14,6 @@

# build regexps that tornado uses:
path_pat = re.compile('^' + '/x%s' % path_regex + '$')
nb_path_pat = re.compile('^' + '/y%s' % notebook_path_regex + '$')

def test_path_regex():
for path in (
Expand All @@ -39,23 +38,3 @@ def test_path_regex_bad():
'/y/x/foo',
):
assert_not_regex(path, path_pat)

def test_notebook_path_regex():
for path in (
'/y/asdf.ipynb',
'/y/foo/bar.ipynb',
'/y/a/b/c/d/e.ipynb',
):
assert_regex(path, nb_path_pat)

def test_notebook_path_regex_bad():
for path in (
'/y',
'/y/',
'/y/.ipynb',
'/y/foo/.ipynb',
'/y/foo/bar',
'/yfoo.ipynb',
'/yfoo/bar.ipynb',
):
assert_not_regex(path, nb_path_pat)

0 comments on commit 3c0bbde

Please sign in to comment.