##// END OF EJS Templates
move url_[un]escape to utils from nbm
MinRK -
Show More
@@ -0,0 +1,61 b''
1 """Test HTML utils"""
2
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2013 The IPython Development Team
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
9
10 #-----------------------------------------------------------------------------
11 # Imports
12 #-----------------------------------------------------------------------------
13
14 import nose.tools as nt
15
16 import IPython.testing.tools as tt
17 from IPython.html.utils import url_escape, url_unescape
18
19 #-----------------------------------------------------------------------------
20 # Test functions
21 #-----------------------------------------------------------------------------
22
23 def test_help_output():
24 """ipython notebook --help-all works"""
25 tt.help_all_output_test('notebook')
26
27
28 def test_url_escape():
29
30 # changes path or notebook name with special characters to url encoding
31 # these tests specifically encode paths with spaces
32 path = url_escape('/this is a test/for spaces/')
33 nt.assert_equal(path, '/this%20is%20a%20test/for%20spaces/')
34
35 path = url_escape('notebook with space.ipynb')
36 nt.assert_equal(path, 'notebook%20with%20space.ipynb')
37
38 path = url_escape('/path with a/notebook and space.ipynb')
39 nt.assert_equal(path, '/path%20with%20a/notebook%20and%20space.ipynb')
40
41 path = url_escape('/ !@$#%^&* / test %^ notebook @#$ name.ipynb')
42 nt.assert_equal(path,
43 '/%20%21%40%24%23%25%5E%26%2A%20/%20test%20%25%5E%20notebook%20%40%23%24%20name.ipynb')
44
45 def test_url_unescape():
46
47 # decodes a url string to a plain string
48 # these tests decode paths with spaces
49 path = url_unescape('/this%20is%20a%20test/for%20spaces/')
50 nt.assert_equal(path, '/this is a test/for spaces/')
51
52 path = url_unescape('notebook%20with%20space.ipynb')
53 nt.assert_equal(path, 'notebook with space.ipynb')
54
55 path = url_unescape('/path%20with%20a/notebook%20and%20space.ipynb')
56 nt.assert_equal(path, '/path with a/notebook and space.ipynb')
57
58 path = url_unescape(
59 '/%20%21%40%24%23%25%5E%26%2A%20/%20test%20%25%5E%20notebook%20%40%23%24%20name.ipynb')
60 nt.assert_equal(path, '/ !@$#%^&* / test %^ notebook @#$ name.ipynb')
61
@@ -1,103 +1,103 b''
1 1 """Tornado handlers for the live notebook view.
2 2
3 3 Authors:
4 4
5 5 * Brian Granger
6 6 """
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2011 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 import os
20 20 from tornado import web
21 21 HTTPError = web.HTTPError
22 22 from zmq.utils import jsonapi
23 23
24 24
25 25 from ..base.handlers import IPythonHandler
26 26 from ..services.notebooks.handlers import _notebook_path_regex, _path_regex
27 from ..utils import url_path_join
27 from ..utils import url_path_join, url_escape, url_unescape
28 28 from urllib import quote
29 29
30 30 #-----------------------------------------------------------------------------
31 31 # Handlers
32 32 #-----------------------------------------------------------------------------
33 33
34 34
35 35 class NotebookHandler(IPythonHandler):
36 36
37 37 @web.authenticated
38 38 def post(self):
39 39 """post either creates a new notebook if no json data is
40 40 sent to the server, or copies the data and returns a
41 41 copied notebook."""
42 42 nbm = self.notebook_manager
43 43 data=self.request.body
44 44 if data:
45 45 data = jsonapi.loads(data)
46 46 notebook_name = nbm.copy_notebook(data['name'])
47 47 else:
48 48 notebook_name = nbm.new_notebook()
49 49 self.finish(jsonapi.dumps({"name": notebook_name}))
50 50
51 51
52 52 class NamedNotebookHandler(IPythonHandler):
53 53
54 54 @web.authenticated
55 55 def get(self, path='', name=None):
56 56 """get renders the notebook template if a name is given, or
57 57 redirects to the '/files/' handler if the name is not given."""
58 58 nbm = self.notebook_manager
59 59 if name is None:
60 60 url = url_path_join(self.base_project_url, 'files', path)
61 61 self.redirect(url)
62 62 return
63 63
64 64 # a .ipynb filename was given
65 65 if not nbm.notebook_exists(name, path):
66 66 raise web.HTTPError(404, u'Notebook does not exist: %s/%s' % (path, name))
67 name = nbm.url_encode(name)
68 path = nbm.url_encode(path)
67 name = url_escape(name)
68 path = url_escape(path)
69 69 self.write(self.render_template('notebook.html',
70 70 project=self.project_dir,
71 71 notebook_path=path,
72 72 notebook_name=name,
73 73 kill_kernel=False,
74 74 mathjax_url=self.mathjax_url,
75 75 )
76 76 )
77 77
78 78 @web.authenticated
79 79 def post(self, path='', name=None):
80 80 """post either creates a new notebook if no json data is
81 81 sent to the server, or copies the data and returns a
82 82 copied notebook in the location given by 'notebook_path."""
83 83 nbm = self.notebook_manager
84 84 data = self.request.body
85 85 if data:
86 86 data = jsonapi.loads(data)
87 87 notebook_name = nbm.copy_notebook(data['name'], notebook_path)
88 88 else:
89 89 notebook_name = nbm.new_notebook(notebook_path)
90 90 self.finish(jsonapi.dumps({"name": notebook_name}))
91 91
92 92
93 93 #-----------------------------------------------------------------------------
94 94 # URL to handler mappings
95 95 #-----------------------------------------------------------------------------
96 96
97 97
98 98 default_handlers = [
99 99 (r"/notebooks/?%s" % _notebook_path_regex, NamedNotebookHandler),
100 100 (r"/notebooks/?%s" % _path_regex, NamedNotebookHandler),
101 101 (r"/notebooks/?", NotebookHandler),
102 102 ]
103 103
@@ -1,211 +1,199 b''
1 1 """A base class notebook manager.
2 2
3 3 Authors:
4 4
5 5 * Brian Granger
6 6 * Zach Sailer
7 7 """
8 8
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (C) 2011 The IPython Development Team
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Imports
18 18 #-----------------------------------------------------------------------------
19 19
20 20 import os
21 21 import uuid
22 22 from urllib import quote, unquote
23 23
24 24 from tornado import web
25 25
26 26 from IPython.html.utils import url_path_join
27 27 from IPython.config.configurable import LoggingConfigurable
28 28 from IPython.nbformat import current
29 29 from IPython.utils.traitlets import List, Dict, Unicode, TraitError
30 30
31 31 #-----------------------------------------------------------------------------
32 32 # Classes
33 33 #-----------------------------------------------------------------------------
34 34
35 35 class NotebookManager(LoggingConfigurable):
36 36
37 37 # Todo:
38 38 # The notebook_dir attribute is used to mean a couple of different things:
39 39 # 1. Where the notebooks are stored if FileNotebookManager is used.
40 40 # 2. The cwd of the kernel for a project.
41 41 # Right now we use this attribute in a number of different places and
42 42 # we are going to have to disentangle all of this.
43 43 notebook_dir = Unicode(os.getcwdu(), config=True, help="""
44 44 The directory to use for notebooks.
45 45 """)
46 46
47 47 filename_ext = Unicode(u'.ipynb')
48 48
49 49 def path_exists(self, path):
50 50 """Does the API-style path (directory) actually exist?
51 51
52 52 Override this method for non-filesystem-based notebooks.
53 53
54 54 Parameters
55 55 ----------
56 56 path : string
57 57 The
58 58
59 59 Returns
60 60 -------
61 61 exists : bool
62 62 Whether the path does indeed exist.
63 63 """
64 64 os_path = self.get_os_path(name, path)
65 65 return os.path.exists(os_path)
66 66
67 67
68 68 def get_os_path(self, name=None, path=''):
69 69 """Given a notebook name and a URL path, return its file system
70 70 path.
71 71
72 72 Parameters
73 73 ----------
74 74 name : string
75 75 The name of a notebook file with the .ipynb extension
76 76 path : string
77 77 The relative URL path (with '/' as separator) to the named
78 78 notebook.
79 79
80 80 Returns
81 81 -------
82 82 path : string
83 83 A file system path that combines notebook_dir (location where
84 84 server started), the relative path, and the filename with the
85 85 current operating system's url.
86 86 """
87 87 parts = path.strip('/').split('/')
88 88 parts = [p for p in parts if p != ''] # remove duplicate splits
89 89 if name is not None:
90 90 parts.append(name)
91 91 path = os.path.join(self.notebook_dir, *parts)
92 92 return path
93 93
94 def url_encode(self, path):
95 """Takes a URL path with special characters and returns
96 the path with all these characters URL encoded"""
97 parts = path.split('/')
98 return '/'.join([quote(p) for p in parts])
99
100 def url_decode(self, path):
101 """Takes a URL path with encoded special characters and
102 returns the URL with special characters decoded"""
103 parts = path.split('/')
104 return '/'.join([unquote(p) for p in parts])
105
106 94 def _notebook_dir_changed(self, name, old, new):
107 95 """Do a bit of validation of the notebook dir."""
108 96 if not os.path.isabs(new):
109 97 # If we receive a non-absolute path, make it absolute.
110 98 self.notebook_dir = os.path.abspath(new)
111 99 return
112 100 if os.path.exists(new) and not os.path.isdir(new):
113 101 raise TraitError("notebook dir %r is not a directory" % new)
114 102 if not os.path.exists(new):
115 103 self.log.info("Creating notebook dir %s", new)
116 104 try:
117 105 os.mkdir(new)
118 106 except:
119 107 raise TraitError("Couldn't create notebook dir %r" % new)
120 108
121 109 # Main notebook API
122 110
123 111 def increment_filename(self, basename, path=''):
124 112 """Increment a notebook filename without the .ipynb to make it unique.
125 113
126 114 Parameters
127 115 ----------
128 116 basename : unicode
129 117 The name of a notebook without the ``.ipynb`` file extension.
130 118 path : unicode
131 119 The URL path of the notebooks directory
132 120 """
133 121 return basename
134 122
135 123 def list_notebooks(self):
136 124 """Return a list of notebook dicts without content.
137 125
138 126 This returns a list of dicts, each of the form::
139 127
140 128 dict(notebook_id=notebook,name=name)
141 129
142 130 This list of dicts should be sorted by name::
143 131
144 132 data = sorted(data, key=lambda item: item['name'])
145 133 """
146 134 raise NotImplementedError('must be implemented in a subclass')
147 135
148 136 def get_notebook_model(self, name, path='', content=True):
149 137 """Get the notebook model with or without content."""
150 138 raise NotImplementedError('must be implemented in a subclass')
151 139
152 140 def save_notebook_model(self, model, name, path=''):
153 141 """Save the notebook model and return the model with no content."""
154 142 raise NotImplementedError('must be implemented in a subclass')
155 143
156 144 def update_notebook_model(self, model, name, path=''):
157 145 """Update the notebook model and return the model with no content."""
158 146 raise NotImplementedError('must be implemented in a subclass')
159 147
160 148 def delete_notebook_model(self, name, path):
161 149 """Delete notebook by name and path."""
162 150 raise NotImplementedError('must be implemented in a subclass')
163 151
164 152 def create_notebook_model(self, model=None, path=''):
165 153 """Create a new untitled notebook and return its model with no content."""
166 154 name = self.increment_filename('Untitled', path)
167 155 if model is None:
168 156 model = {}
169 157 metadata = current.new_metadata(name=u'')
170 158 nb = current.new_notebook(metadata=metadata)
171 159 model['content'] = nb
172 160 model['name'] = name
173 161 model['path'] = path
174 162 model = self.save_notebook_model(model, name, path)
175 163 return model
176 164
177 165 def copy_notebook(self, name, path='/', content=False):
178 166 """Copy an existing notebook and return its new model."""
179 167 model = self.get_notebook_model(name, path)
180 168 name = os.path.splitext(name)[0] + '-Copy'
181 169 name = self.increment_filename(name, path) + self.filename_ext
182 170 model['name'] = name
183 171 model = self.save_notebook_model(model, name, path, content=content)
184 172 return model
185 173
186 174 # Checkpoint-related
187 175
188 176 def create_checkpoint(self, name, path='/'):
189 177 """Create a checkpoint of the current state of a notebook
190 178
191 179 Returns a checkpoint_id for the new checkpoint.
192 180 """
193 181 raise NotImplementedError("must be implemented in a subclass")
194 182
195 183 def list_checkpoints(self, name, path='/'):
196 184 """Return a list of checkpoints for a given notebook"""
197 185 return []
198 186
199 187 def restore_checkpoint(self, checkpoint_id, name, path='/'):
200 188 """Restore a notebook from one of its checkpoints"""
201 189 raise NotImplementedError("must be implemented in a subclass")
202 190
203 191 def delete_checkpoint(self, checkpoint_id, name, path='/'):
204 192 """delete a checkpoint for a notebook"""
205 193 raise NotImplementedError("must be implemented in a subclass")
206 194
207 195 def log_info(self):
208 196 self.log.info(self.info_string())
209 197
210 198 def info_string(self):
211 199 return "Serving notebooks"
@@ -1,246 +1,210 b''
1 1 """Tests for the notebook manager."""
2 2
3 3 import os
4 4
5 5 from tornado.web import HTTPError
6 6 from unittest import TestCase
7 7 from tempfile import NamedTemporaryFile
8 8
9 9 from IPython.utils.tempdir import TemporaryDirectory
10 10 from IPython.utils.traitlets import TraitError
11 11 from IPython.html.utils import url_path_join
12 12
13 13 from ..filenbmanager import FileNotebookManager
14 14 from ..nbmanager import NotebookManager
15 15
16 16 class TestFileNotebookManager(TestCase):
17 17
18 18 def test_nb_dir(self):
19 19 with TemporaryDirectory() as td:
20 20 fm = FileNotebookManager(notebook_dir=td)
21 21 self.assertEqual(fm.notebook_dir, td)
22 22
23 23 def test_create_nb_dir(self):
24 24 with TemporaryDirectory() as td:
25 25 nbdir = os.path.join(td, 'notebooks')
26 26 fm = FileNotebookManager(notebook_dir=nbdir)
27 27 self.assertEqual(fm.notebook_dir, nbdir)
28 28
29 29 def test_missing_nb_dir(self):
30 30 with TemporaryDirectory() as td:
31 31 nbdir = os.path.join(td, 'notebook', 'dir', 'is', 'missing')
32 32 self.assertRaises(TraitError, FileNotebookManager, notebook_dir=nbdir)
33 33
34 34 def test_invalid_nb_dir(self):
35 35 with NamedTemporaryFile() as tf:
36 36 self.assertRaises(TraitError, FileNotebookManager, notebook_dir=tf.name)
37 37
38 38 def test_get_os_path(self):
39 39 # full filesystem path should be returned with correct operating system
40 40 # separators.
41 41 with TemporaryDirectory() as td:
42 42 nbdir = os.path.join(td, 'notebooks')
43 43 fm = FileNotebookManager(notebook_dir=nbdir)
44 44 path = fm.get_os_path('test.ipynb', '/path/to/notebook/')
45 45 rel_path_list = '/path/to/notebook/test.ipynb'.split('/')
46 46 fs_path = os.path.join(fm.notebook_dir, *rel_path_list)
47 47 self.assertEqual(path, fs_path)
48 48
49 49 fm = FileNotebookManager(notebook_dir=nbdir)
50 50 path = fm.get_os_path('test.ipynb')
51 51 fs_path = os.path.join(fm.notebook_dir, 'test.ipynb')
52 52 self.assertEqual(path, fs_path)
53 53
54 54 fm = FileNotebookManager(notebook_dir=nbdir)
55 55 path = fm.get_os_path('test.ipynb', '////')
56 56 fs_path = os.path.join(fm.notebook_dir, 'test.ipynb')
57 57 self.assertEqual(path, fs_path)
58 58
59 59 class TestNotebookManager(TestCase):
60 60
61 61 def make_dir(self, abs_path, rel_path):
62 62 """make subdirectory, rel_path is the relative path
63 63 to that directory from the location where the server started"""
64 64 os_path = os.path.join(abs_path, rel_path)
65 65 try:
66 66 os.makedirs(os_path)
67 67 except OSError:
68 68 print "Directory already exists."
69 69
70 def test_url_encode(self):
71 nm = NotebookManager()
72
73 # changes path or notebook name with special characters to url encoding
74 # these tests specifically encode paths with spaces
75 path = nm.url_encode('/this is a test/for spaces/')
76 self.assertEqual(path, '/this%20is%20a%20test/for%20spaces/')
77
78 path = nm.url_encode('notebook with space.ipynb')
79 self.assertEqual(path, 'notebook%20with%20space.ipynb')
80
81 path = nm.url_encode('/path with a/notebook and space.ipynb')
82 self.assertEqual(path, '/path%20with%20a/notebook%20and%20space.ipynb')
83
84 path = nm.url_encode('/ !@$#%^&* / test %^ notebook @#$ name.ipynb')
85 self.assertEqual(path,
86 '/%20%21%40%24%23%25%5E%26%2A%20/%20test%20%25%5E%20notebook%20%40%23%24%20name.ipynb')
87
88 def test_url_decode(self):
89 nm = NotebookManager()
90
91 # decodes a url string to a plain string
92 # these tests decode paths with spaces
93 path = nm.url_decode('/this%20is%20a%20test/for%20spaces/')
94 self.assertEqual(path, '/this is a test/for spaces/')
95
96 path = nm.url_decode('notebook%20with%20space.ipynb')
97 self.assertEqual(path, 'notebook with space.ipynb')
98
99 path = nm.url_decode('/path%20with%20a/notebook%20and%20space.ipynb')
100 self.assertEqual(path, '/path with a/notebook and space.ipynb')
101
102 path = nm.url_decode(
103 '/%20%21%40%24%23%25%5E%26%2A%20/%20test%20%25%5E%20notebook%20%40%23%24%20name.ipynb')
104 self.assertEqual(path, '/ !@$#%^&* / test %^ notebook @#$ name.ipynb')
105
106 70 def test_create_notebook_model(self):
107 71 with TemporaryDirectory() as td:
108 72 # Test in root directory
109 73 nm = FileNotebookManager(notebook_dir=td)
110 74 model = nm.create_notebook_model()
111 75 assert isinstance(model, dict)
112 76 self.assertIn('name', model)
113 77 self.assertIn('path', model)
114 78 self.assertEqual(model['name'], 'Untitled0.ipynb')
115 79 self.assertEqual(model['path'], '/')
116 80
117 81 # Test in sub-directory
118 82 sub_dir = '/foo/'
119 83 self.make_dir(nm.notebook_dir, 'foo')
120 84 model = nm.create_notebook_model(None, sub_dir)
121 85 assert isinstance(model, dict)
122 86 self.assertIn('name', model)
123 87 self.assertIn('path', model)
124 88 self.assertEqual(model['name'], 'Untitled0.ipynb')
125 89 self.assertEqual(model['path'], sub_dir)
126 90
127 91 def test_get_notebook_model(self):
128 92 with TemporaryDirectory() as td:
129 93 # Test in root directory
130 94 # Create a notebook
131 95 nm = FileNotebookManager(notebook_dir=td)
132 96 model = nm.create_notebook_model()
133 97 name = model['name']
134 98 path = model['path']
135 99
136 100 # Check that we 'get' on the notebook we just created
137 101 model2 = nm.get_notebook_model(name, path)
138 102 assert isinstance(model2, dict)
139 103 self.assertIn('name', model2)
140 104 self.assertIn('path', model2)
141 105 self.assertEqual(model['name'], name)
142 106 self.assertEqual(model['path'], path)
143 107
144 108 # Test in sub-directory
145 109 sub_dir = '/foo/'
146 110 self.make_dir(nm.notebook_dir, 'foo')
147 111 model = nm.create_notebook_model(None, sub_dir)
148 112 model2 = nm.get_notebook_model(name, sub_dir)
149 113 assert isinstance(model2, dict)
150 114 self.assertIn('name', model2)
151 115 self.assertIn('path', model2)
152 116 self.assertIn('content', model2)
153 117 self.assertEqual(model2['name'], 'Untitled0.ipynb')
154 118 self.assertEqual(model2['path'], sub_dir)
155 119
156 120 def test_update_notebook_model(self):
157 121 with TemporaryDirectory() as td:
158 122 # Test in root directory
159 123 # Create a notebook
160 124 nm = FileNotebookManager(notebook_dir=td)
161 125 model = nm.create_notebook_model()
162 126 name = model['name']
163 127 path = model['path']
164 128
165 129 # Change the name in the model for rename
166 130 model['name'] = 'test.ipynb'
167 131 model = nm.update_notebook_model(model, name, path)
168 132 assert isinstance(model, dict)
169 133 self.assertIn('name', model)
170 134 self.assertIn('path', model)
171 135 self.assertEqual(model['name'], 'test.ipynb')
172 136
173 137 # Make sure the old name is gone
174 138 self.assertRaises(HTTPError, nm.get_notebook_model, name, path)
175 139
176 140 # Test in sub-directory
177 141 # Create a directory and notebook in that directory
178 142 sub_dir = '/foo/'
179 143 self.make_dir(nm.notebook_dir, 'foo')
180 144 model = nm.create_notebook_model(None, sub_dir)
181 145 name = model['name']
182 146 path = model['path']
183 147
184 148 # Change the name in the model for rename
185 149 model['name'] = 'test_in_sub.ipynb'
186 150 model = nm.update_notebook_model(model, name, path)
187 151 assert isinstance(model, dict)
188 152 self.assertIn('name', model)
189 153 self.assertIn('path', model)
190 154 self.assertEqual(model['name'], 'test_in_sub.ipynb')
191 155 self.assertEqual(model['path'], sub_dir)
192 156
193 157 # Make sure the old name is gone
194 158 self.assertRaises(HTTPError, nm.get_notebook_model, name, path)
195 159
196 160 def test_save_notebook_model(self):
197 161 with TemporaryDirectory() as td:
198 162 # Test in the root directory
199 163 # Create a notebook
200 164 nm = FileNotebookManager(notebook_dir=td)
201 165 model = nm.create_notebook_model()
202 166 name = model['name']
203 167 path = model['path']
204 168
205 169 # Get the model with 'content'
206 170 full_model = nm.get_notebook_model(name, path)
207 171
208 172 # Save the notebook
209 173 model = nm.save_notebook_model(full_model, name, path)
210 174 assert isinstance(model, dict)
211 175 self.assertIn('name', model)
212 176 self.assertIn('path', model)
213 177 self.assertEqual(model['name'], name)
214 178 self.assertEqual(model['path'], path)
215 179
216 180 # Test in sub-directory
217 181 # Create a directory and notebook in that directory
218 182 sub_dir = '/foo/'
219 183 self.make_dir(nm.notebook_dir, 'foo')
220 184 model = nm.create_notebook_model(None, sub_dir)
221 185 name = model['name']
222 186 path = model['path']
223 187 model = nm.get_notebook_model(name, path)
224 188
225 189 # Change the name in the model for rename
226 190 model = nm.save_notebook_model(model, name, path)
227 191 assert isinstance(model, dict)
228 192 self.assertIn('name', model)
229 193 self.assertIn('path', model)
230 194 self.assertEqual(model['name'], 'Untitled0.ipynb')
231 195 self.assertEqual(model['path'], sub_dir)
232 196
233 197 def test_delete_notebook_model(self):
234 198 with TemporaryDirectory() as td:
235 199 # Test in the root directory
236 200 # Create a notebook
237 201 nm = FileNotebookManager(notebook_dir=td)
238 202 model = nm.create_notebook_model()
239 203 name = model['name']
240 204 path = model['path']
241 205
242 206 # Delete the notebook
243 207 nm.delete_notebook_model(name, path)
244 208
245 209 # Check that a 'get' on the deleted notebook raises and error
246 210 self.assertRaises(HTTPError, nm.get_notebook_model, name, path)
@@ -1,51 +1,66 b''
1 1 """Notebook related utilities
2 2
3 3 Authors:
4 4
5 5 * Brian Granger
6 6 """
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2011 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 import os
16 16 from urllib import quote, unquote
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 22 def url_path_join(*pieces):
23 23 """Join components of url into a relative url
24 24
25 25 Use to prevent double slash when joining subpath. This will leave the
26 26 initial and final / in place
27 27 """
28 28 initial = pieces[0].startswith('/')
29 29 final = pieces[-1].endswith('/')
30 30 stripped = [s.strip('/') for s in pieces]
31 31 result = '/'.join(s for s in stripped if s)
32 32 if initial: result = '/' + result
33 33 if final: result = result + '/'
34 34 if result == '//': result = '/'
35 35 return result
36 36
37 37 def path2url(path):
38 38 """Convert a local file path to a URL"""
39 39 pieces = [ quote(p) for p in path.split(os.path.sep) ]
40 40 # preserve trailing /
41 41 if pieces[-1] == '':
42 42 pieces[-1] = '/'
43 43 url = url_path_join(*pieces)
44 44 return url
45 45
46 46 def url2path(url):
47 47 """Convert a URL to a local file path"""
48 48 pieces = [ unquote(p) for p in url.split('/') ]
49 49 path = os.path.join(*pieces)
50 50 return path
51 51
52 def url_escape(path):
53 """Escape special characters in a URL path
54
55 Turns '/foo bar/' into '/foo%20bar/'
56 """
57 parts = path.split('/')
58 return '/'.join([quote(p) for p in parts])
59
60 def url_unescape(path):
61 """Unescape special characters in a URL path
62
63 Turns '/foo%20bar/' into '/foo bar/'
64 """
65 return '/'.join([unquote(p) for p in path.split('/')])
66
General Comments 0
You need to be logged in to leave comments. Login now