# HG changeset patch # User Mitesh Patel # Date 1262813598 28800 # Node ID c6ea40a1660c8effb509bcaeea18827d30ac3baf # Parent f20b011f30dc307d9f79627a78f01150005db3d3 #7249 Switch the notebook's templating system to Jinja2 diff --git a/sagenb/data/sage/css/main.css b/sagenb/data/sage/css/main.css --- a/sagenb/data/sage/css/main.css +++ b/sagenb/data/sage/css/main.css @@ -37,8 +37,8 @@ hr.usercontrol { clear: both; float: left; } -div#user-controls { overflow: hidden; display: inline-block; clear: both; border-bottom: 1px solid #c9d7f1; padding: 10px; } -div#user-controls { display: block; } +div#user-main-controls { overflow: hidden; display: inline-block; clear: both; border-bottom: 1px solid #c9d7f1; padding: 10px; } +div#user-main-controls { display: block; } div#worksheet-list-controls { padding: 10px; clear: both; overflow: hidden; display: inline-block; } div#worksheet-list-controls { display: block; } div#worksheet-list-controls div.action-buttons { float: left; } @@ -305,9 +305,12 @@ span.worksheet_buttons { position: relative; top: -20ex; right: 0ex; } + .thin-right { position: absolute; top: auto; right: 0; width: 70%; } /* *********** User Home (Worksheet listing) ************************* */ +#worksheet-listing-page #welcome-message { text-align: center; padding: 1em; } + .ratingmsg { color: #112abb; padding: 0.3em; font-size: 14px; } .pubmsg { font-family: sans-serif; color: #112abb; padding: 0.3em; font-size: 12px; } diff --git a/sagenb/data/sage/html/base.html b/sagenb/data/sage/html/base.html --- a/sagenb/data/sage/html/base.html +++ b/sagenb/data/sage/html/base.html @@ -1,4 +1,4 @@ -{%- macro render_title -%}{% block title %}{% endblock %}{%- endmacro -%} +{%- macro render_title() -%}{% block title %}{% endblock %}{%- endmacro -%} diff --git a/sagenb/data/sage/html/notebook/cell.html b/sagenb/data/sage/html/notebook/cell.html --- a/sagenb/data/sage/html/notebook/cell.html +++ b/sagenb/data/sage/html/notebook/cell.html @@ -12,12 +12,15 @@ print or not. #} {% if do_print %} - {% set wrap = 68 %} - {% set div_wrap = true %} + {% set wrap_ = 68 %} + {% set div_wrap_ = true %} +{% else %} + {% set wrap_ = wrap %} + {% set div_wrap_ = div_wrap %} {% endif %} {% set cell_cls = "cell_evaluated" if cell.evaluated() or do_print else "cell_not_evaluated" %} -{% if div_wrap %} +{% if div_wrap_ %}
{% endif %} @@ -56,7 +59,7 @@ onFocus="cell_focused(this, {{ cell.id() }}); return true;">{{ cell.input_text().rstrip() }} evaluate @@ -85,7 +88,7 @@ {% if cell.introspect() %} {{ cell.output_text(0, html=true) }} {% else %} - {{ cell.output_text(wrap, html=true) }} + {{ cell.output_text(wrap_, html=true) }} {% endif %}
{% if not do_print %} @@ -105,7 +108,7 @@
{% endif %} -{% if div_wrap %} +{% if div_wrap_ %} {% endif %} diff --git a/sagenb/data/sage/html/worksheet_listing.html b/sagenb/data/sage/html/worksheet_listing.html --- a/sagenb/data/sage/html/worksheet_listing.html +++ b/sagenb/data/sage/html/worksheet_listing.html @@ -130,7 +130,7 @@ {% elif typ == 'active' %} - + Welcome to Sage! You can create a new worksheet, view published worksheets, or read the documentation. diff --git a/sagenb/interfaces/expect.py b/sagenb/interfaces/expect.py --- a/sagenb/interfaces/expect.py +++ b/sagenb/interfaces/expect.py @@ -241,11 +241,11 @@ # The magic comment at the very start of the file allows utf8 characters. open(self._filename,'w').write( '# -*- coding: utf_8 -*-\nimport sys;sys.ps1="%s";print "START%s"\n'%( - self._prompt, self._number) + displayhook_hack(string)) + self._prompt, self._number) + displayhook_hack(string).encode('utf-8', 'ignore')) try: self._expect.sendline('\nimport os;os.chdir("%s");\nexecfile("%s")'%( remote, sage_input)) - except OSError, msg: + except OSError as msg: self._is_computing = False self._so_far = str(msg) diff --git a/sagenb/misc/misc.py b/sagenb/misc/misc.py --- a/sagenb/misc/misc.py +++ b/sagenb/misc/misc.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Miscellaneous Notebook Functions """ @@ -384,7 +385,6 @@ # TODO raise NotImplementedError, "Curently %cython mode requires Sage." - ############################################################# # File permissions # May need some changes on Windows. @@ -402,6 +402,45 @@ stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | \ stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP) +def encoded_str(obj, encoding='utf-8'): + """ + Takes an object and returns an encoded str human-readable representation. + + EXAMPLES:: + + sage: from sagenb.misc.misc import encoded_str + sage: encoded_str(u'ĀƂḉПΣ') + '\xc3\x84\xc2\x80\xc3\x86\xc2\x82\xc3\xa1\xc2\xb8\xc2\x89\xc3\x90\xc2\x9f\xc3\x8e\xc2\xa3' + sage: encoded_str(u'abc') + 'abc' + sage: encoded_str(123) + '123' + """ + if isinstance(obj, unicode): + return obj.encode(encoding, 'ignore') + return str(obj) + +def unicode_str(obj, encoding='utf-8'): + """ + Takes an object and returns a unicode human-readable representation. + + EXAMPLES:: + + sage: from sagenb.misc.misc import unicode_str + sage: unicode_str('\xc3\x84\xc2\x80\xc3\x86\xc2\x82\xc3\xa1\xc2\xb8\xc2\x89\xc3\x90\xc2\x9f\xc3\x8e\xc2\xa3') + u'\xc4\x80\xc6\x82\xe1\xb8\x89\xd0\x9f\xce\xa3' + sage: unicode_str('\xc3\x84\xc2\x80\xc3\x86\xc2\x82\xc3\xa1\xc2\xb8\xc2\x89\xc3\x90\xc2\x9f\xc3\x8e\xc2\xa3') == u'ĀƂḉПΣ' + True + sage: unicode_str('abc') + u'abc' + sage: unicode_str(123) + u'123' + """ + if isinstance(obj, str): + return obj.decode(encoding, 'ignore') + return unicode(str(obj), encoding) + + def ignore_nonexistent_files(curdir, dirlist): """ diff --git a/sagenb/misc/sageinspect.py b/sagenb/misc/sageinspect.py --- a/sagenb/misc/sageinspect.py +++ b/sagenb/misc/sageinspect.py @@ -483,8 +483,11 @@ if r is None: return '' - - s = format(str(r), embedded=EMBEDDED_MODE) + if isinstance(r, unicode): + r = r.encode('utf-8', 'ignore') + else: + r = str(r) + s = format(r, embedded=EMBEDDED_MODE) # If there is a Cython embedded position, it needs to be stripped pos = _extract_embedded_position(s) diff --git a/sagenb/misc/support.py b/sagenb/misc/support.py --- a/sagenb/misc/support.py +++ b/sagenb/misc/support.py @@ -331,7 +331,7 @@ filename = sageinspect.sage_getfile(obj) try: lines, lineno = sageinspect.sage_getsourcelines(obj, is_binary=False) - except IOError, msg: + except IOError as msg: return html_markup(str(msg)) src = indent.join(lines) src = indent + format_src(src) @@ -467,6 +467,8 @@ if dir: if hasattr(system.__class__, 'chdir'): system.chdir(dir) + if isinstance(cmd, unicode): + cmd = cmd.encode('utf-8', 'ignore') return system.eval(cmd, sage_globals, locals = sage_globals) ###################################################################### diff --git a/sagenb/notebook/cell.py b/sagenb/notebook/cell.py --- a/sagenb/notebook/cell.py +++ b/sagenb/notebook/cell.py @@ -18,7 +18,8 @@ from sagenb.misc.misc import (word_wrap, SAGE_DOC, strip_string_literals, - set_restrictive_permissions) + set_restrictive_permissions, + unicode_str) from jsmath import math_parse @@ -91,6 +92,7 @@ sage: C == loads(dumps(C)) True """ + text = unicode_str(text) self.__id = int(id) self.__text = text self.__worksheet = worksheet @@ -103,7 +105,7 @@ sage: C = sagenb.notebook.cell.TextCell(0, '2+3', None) sage: C.__repr__() - 'TextCell 0: 2+3' + u'TextCell 0: 2+3' """ return "TextCell %s: %s"%(self.__id, self.__text) @@ -136,6 +138,7 @@ sage: C TextCell 0: 3+2 """ + input_text = unicode_str(input_text) self.__text = input_text def set_worksheet(self, worksheet, id=None): @@ -179,7 +182,7 @@ - ``wrap`` -- number of columns to wrap at (not used) - ``div_wrap`` -- whether to wrap in a div (not used) - + - ``do_math_parse`` - bool (default: True) If True, call math_parse (defined in cell.py) on the html. @@ -192,7 +195,10 @@ EXAMPLES:: - sage: C = sagenb.notebook.cell.TextCell(0, '2+3', None) + sage: nb = sagenb.notebook.notebook.Notebook(tmp_dir()+'.sagenb') + sage: nb.add_user('sage','sage','sage@sagemath.org',force=True) + sage: W = nb.create_new_worksheet('Test', 'sage') + sage: C = sagenb.notebook.cell.TextCell(0, '2+3', W) sage: C.html() u'...text_cell...2+3...' sage: C.set_input_text("$2+3$") @@ -214,7 +220,7 @@ sage: C = sagenb.notebook.cell.TextCell(0, '2+3', None) sage: C.plain_text() - '2+3' + u'2+3' """ return self.__text @@ -226,7 +232,7 @@ sage: C = sagenb.notebook.cell.TextCell(0, '2+3', None) sage: C.edit_text() - '2+3' + u'2+3' """ return self.__text @@ -297,8 +303,11 @@ sage: C == loads(dumps(C)) True """ + out = unicode_str(out) + input = unicode_str(input) + self.__id = int(id) - self.__out = str(out).replace('\r','') + self.__out = out.replace('\r','') self.__worksheet = worksheet self.__interrupted = False self.__completions = False @@ -355,8 +364,8 @@ sage: C Cell 0; in=2+3, out= """ - self.__out = '' - self.__out_html = '' + self.__out = u'' + self.__out_html = u'' self.__evaluated = False def evaluated(self): @@ -555,7 +564,7 @@ sage: nb.delete() """ if self.is_interactive_cell(): - self.__out_html = "" + self.__out_html = u"" else: self.__out_html = self.files_html(output) @@ -735,9 +744,12 @@ """ if ncols == 0: ncols = self.word_wrap_cols() - s = '' + s = u'' + + self.__in = unicode_str(self.__in) input_lines = self.__in + pr = 'sage: ' if prompts: @@ -810,10 +822,10 @@ sage: C = sagenb.notebook.cell.Cell(0, '2+3', '5', None) sage: C.edit_text() - '{{{id=0|\n2+3\n///\n5\n}}}' + u'{{{id=0|\n2+3\n///\n5\n}}}' """ s = self.plain_text(ncols, prompts, max_out) - return '{{{id=%s|\n%s\n}}}'%(self.id(), s) + return u'{{{id=%s|\n%s\n}}}'%(self.id(), s) def is_last(self): """ @@ -988,7 +1000,7 @@ 0 sage: C.set_input_text('3+3') sage: C.input_text() - '3+3' + u'3+3' sage: C.evaluated() False sage: C.version() @@ -997,6 +1009,8 @@ sage: nb.delete() """ # Stuff to deal with interact + input = unicode_str(input) + if input.startswith('%__sage_interact__'): self.interact = input[len('%__sage_interact__')+1:] self.__version = self.version() + 1 @@ -1028,7 +1042,7 @@ sage: C = sagenb.notebook.cell.Cell(0, '2+3', '5', None) sage: C.input_text() - '2+3' + u'2+3' """ return self.__in @@ -1042,7 +1056,7 @@ sage: C = sagenb.notebook.cell.Cell(0, '%hide\n%maxima\n2+3', '5', None) sage: C.cleaned_input_text() - '2+3' + u'2+3' """ if self.is_interacting(): return self.interact @@ -1060,9 +1074,9 @@ sage: C = sagenb.notebook.cell.Cell(0, '%hide\n%maxima\n2+3', '5', None) sage: C.parse_percent_directives() - '2+3' + u'2+3' sage: C.percent_directives() - ['hide', 'maxima'] + [u'hide', u'maxima'] """ self._system = None text = self.input_text().splitlines() @@ -1097,7 +1111,7 @@ sage: C = sagenb.notebook.cell.Cell(0, '%hide\n%maxima\n2+3', '5', None) sage: C.percent_directives() - ['hide', 'maxima'] + [u'hide', u'maxima'] """ return self._percent_directives @@ -1115,7 +1129,7 @@ sage: C = sagenb.notebook.cell.Cell(0, '%maxima\n2+3', '5', None) sage: C.system() - 'maxima' + u'maxima' sage: prefixes = ['%hide', '%time', ''] sage: cells = [sagenb.notebook.cell.Cell(0, '%s\n2+3'%prefix, '5', None) for prefix in prefixes] sage: [(C, C.system()) for C in cells if C.system() is not None] @@ -1155,9 +1169,9 @@ '' sage: C.set_changed_input_text('3+3') sage: C.input_text() - '3+3' + u'3+3' sage: C.changed_input_text() - '3+3' + u'3+3' sage: C.changed_input_text() '' sage: C.version() @@ -1180,10 +1194,12 @@ sage: C = sagenb.notebook.cell.Cell(0, '2+3', '5', None) sage: C.set_changed_input_text('3+3') sage: C.input_text() - '3+3' + u'3+3' sage: C.changed_input_text() - '3+3' + u'3+3' """ + new_text = unicode_str(new_text) + self.__changed_input = new_text self.__in = new_text @@ -1200,9 +1216,11 @@ sage: len(C.plain_text()) 12 """ + output = unicode_str(output) + html = unicode_str(html) if output.count('') > 1: - html = '

WARNING: multiple @interacts in one cell disabled (not yet implemented).

' - output = '' + html = u'

WARNING: multiple @interacts in one cell disabled (not yet implemented).

' + output = u'' # In interacting mode, we just save the computed output # (do not overwrite). @@ -1222,7 +1240,7 @@ url = "" if not self.computing(): file = os.path.join(self.directory(), "full_output.txt") - open(file,"w").write(output) + open(file,"w").write(output.encode('utf-8', 'ignore')) url = "full_output.txt"%( self.url_to_self()) html+="
" + url @@ -1265,7 +1283,7 @@ '' sage: C.set_output_text('5', '5') sage: C.output_html() - '5' + u'5' """ try: return self.__out_html @@ -1320,11 +1338,11 @@ sage: W = nb.create_new_worksheet('Test', 'sage') sage: C = sagenb.notebook.cell.Cell(0, '2+3', '5', W) sage: C.output_text() - '
5
' + u'
5
' sage: C.output_text(html=False) - '
5
' + u'
5
' sage: C.output_text(raw=True) - '5' + u'5' """ if allow_interact and hasattr(self, '_interact_output'): # Get the input template @@ -1348,13 +1366,15 @@ # during interact. return '' + self.__out = unicode_str(self.__out) + is_interact = self.is_interactive_cell() if is_interact and ncols == 0: if 'Traceback (most recent call last)' in self.__out: s = self.__out.replace('cell-interact','') is_interact=False else: - return '

Click to the left again to hide and once more to show the dynamic interactive window

' + return u'

Click to the left again to hide and once more to show the dynamic interactive window

' else: s = self.__out @@ -1448,7 +1468,7 @@ sage: C = sagenb.notebook.cell.Cell(0, "%html\nTest HTML", None, None) sage: C.system() - 'html' + u'html' sage: C.is_html() True sage: C = sagenb.notebook.cell.Cell(0, "Test HTML", None, None) @@ -1495,13 +1515,15 @@ ('d', Cell 0; in=sage?, out=) sage: C.set_introspect_html('foobar') sage: C.introspect_html() - 'foobar' + u'foobar' sage: C.set_introspect_html('`foobar`') sage: C.introspect_html() - '`foobar`' + u'`foobar`' sage: W.quit() sage: nb.delete() """ + html = unicode_str(html) + self.__introspect_html = html self.introspection_status = 'done' @@ -1542,8 +1564,8 @@ try: return self.__introspect_html except AttributeError: - self.__introspect_html = '' - return '' + self.__introspect_html = u'' + return u'' def introspect(self): """ @@ -1561,7 +1583,7 @@ sage: W.check_comp(9999) # random output -- depends on computer speed ('d', Cell 0; in=sage?, out=) sage: C.introspect() - ['sage?', ''] + [u'sage?', ''] sage: W.quit() sage: nb.delete() """ @@ -1586,7 +1608,7 @@ sage: W.check_comp(9999) # random output -- depends on computer speed ('d', Cell 0; in=sage?, out=) sage: C.introspect() - ['sage?', ''] + [u'sage?', ''] sage: C.unset_introspect() sage: C.introspect() False @@ -1735,7 +1757,7 @@ if wrap is None: wrap = self.notebook().conf()['word_wrap_cols'] - + return template(os.path.join('html', 'notebook', 'cell.html'), cell=self, wrap=wrap, div_wrap=div_wrap, do_print=do_print) @@ -1908,6 +1930,10 @@ files = '' else: files = (' '*3).join(files) + + files = unicode_str(files) + images = unicode_str(images) + return images + files ######## diff --git a/sagenb/notebook/misc.py b/sagenb/notebook/misc.py new file mode 100644 --- /dev/null +++ b/sagenb/notebook/misc.py @@ -0,0 +1,6 @@ +""" +Miscellaneus functions used by the Sage Notebook + + +""" + diff --git a/sagenb/notebook/notebook.py b/sagenb/notebook/notebook.py --- a/sagenb/notebook/notebook.py +++ b/sagenb/notebook/notebook.py @@ -32,7 +32,7 @@ # Sage libraries from sagenb.misc.misc import (pad_zeros, cputime, tmp_dir, load, save, - ignore_nonexistent_files) + ignore_nonexistent_files, encoded_str) # Sage Notebook import css # style @@ -934,8 +934,7 @@ return self._user_history[username] history = [] for hunk in self.__storage.load_user_history(username): - if isinstance(hunk, str): - hunk = unicode(hunk.decode('utf-8', 'ignore')) + hunk = encoded_str(hunk) history.append(hunk) self._user_history[username] = history return history @@ -1052,7 +1051,7 @@ W is our newly-created worksheet, with the 2+3 cell in it:: sage: W.name() - 'foo' + u'foo' sage: W.cell_list() [TextCell 0: foo, Cell 1; in=2+3, out=] """ @@ -1266,7 +1265,7 @@ sage: nb = sagenb.notebook.notebook.Notebook(tmp_dir()+'.sagenb') sage: W = nb.create_new_worksheet('Test', 'admin') sage: W.body() - '\n\n{{{id=1|\n\n///\n}}}' + u'\n\n{{{id=1|\n\n///\n}}}' sage: W.save_snapshot('admin') sage: nb.html_worksheet_revision_list('admin', W) u'...Revision...Last Edited...ago...' @@ -1275,7 +1274,7 @@ return template(os.path.join("html", "notebook", "worksheet_revision_list.html"), data = data, worksheet = worksheet, - worksheet_filename = worksheet.filename(), + notebook = self, username = username) @@ -1347,7 +1346,7 @@ return template(os.path.join("html", "notebook", "worksheet_share.html"), worksheet = worksheet, - worksheet_filename = worksheet.filename(), + notebook = self, username = username, other_users = other_users) def html_download_or_delete_datafile(self, ws, username, filename): @@ -1384,7 +1383,7 @@ text_file_content = open(os.path.join(ws.data_directory(), filename)).read() return template(os.path.join("html", "notebook", "download_or_delete_datafile.html"), - worksheet = ws, worksheet_filename = ws.filename(), + worksheet = ws, notebook = self, username = username, filename_ = filename, file_is_image = file_is_image, @@ -1504,7 +1503,7 @@ return template(os.path.join("html", "notebook", "plain_text_window.html"), worksheet = worksheet, - worksheet_filename = worksheet.filename(), + notebook = self, username = username, plain_text = plain_text, JSMATH = JSMATH, JEDITABLE_TINYMCE = JEDITABLE_TINYMCE) @@ -1532,7 +1531,7 @@ return template(os.path.join("html", "notebook", "edit_window.html"), worksheet = worksheet, - worksheet_filename = worksheet.filename(), + notebook = self, username = username) def html_beforepublish_window(self, worksheet, username): @@ -1569,9 +1568,8 @@ """ return template(os.path.join("html", "notebook", "beforepublish_window.html"), worksheet = worksheet, - worksheet_filename = worksheet.filename(), - username = username, - JSMATH = JSMATH, JEDITABLE_TINYMCE = JEDITABLE_TINYMCE) + notebook = self, + username = username) def html_afterpublish_window(self, worksheet, username, url, dtime): r""" @@ -1598,9 +1596,8 @@ return template(os.path.join("html", "notebook", "afterpublish_window.html"), worksheet = worksheet, - worksheet_filename = worksheet.filename(), - username = username, url = url, time = time, - JSMATH = JSMATH, JEDITABLE_TINYMCE = JEDITABLE_TINYMCE) + notebook = self, + username = username, url = url, time = time) def html_upload_data_window(self, ws, username): r""" diff --git a/sagenb/notebook/template.py b/sagenb/notebook/template.py --- a/sagenb/notebook/template.py +++ b/sagenb/notebook/template.py @@ -15,9 +15,7 @@ # http://www.gnu.org/licenses/ ############################################################################# -import jinja - -from jinja.filters import stringfilter, simplefilter +import jinja2 import os, re, sys @@ -27,11 +25,10 @@ TEMPLATE_PATH = os.path.join(DATA, 'sage') -env = jinja.Environment(loader=jinja.FileSystemLoader(TEMPLATE_PATH)) +env = jinja2.Environment(loader=jinja2.FileSystemLoader(TEMPLATE_PATH)) css_illegal_re = re.compile(r'[^-A-Za-z_0-9]') -@stringfilter def css_escape(string): r""" Returns a string with all characters not legal in a css name @@ -43,32 +40,16 @@ EXAMPLES:: - sage: from sagenb.notebook.template import env, css_escape - sage: escaper = css_escape() - sage: print(escaper(env, {}, '12abcd')) - 12abcd - sage: print(escaper(env, {}, 'abcd')) - abcd - sage: print(escaper(env, {}, r'\'"abcd\'"')) - ---abcd--- - sage: print(escaper(env, {}, 'my-invalid/identifier')) - my-invalid-identifier - sage: print(escaper(env, {}, r'quotes"mustbe!escaped')) - quotes-mustbe-escaped - - The following doctests originally accompanied #7269's support for - Jinja2. - - sage: from sagenb.notebook.template import css_escape # not tested - sage: css_escape('abcd') # not tested + sage: from sagenb.notebook.template import css_escape + sage: css_escape('abcd') 'abcd' - sage: css_escape('12abcd') # not tested + sage: css_escape('12abcd') '12abcd' - sage: css_escape(r'\'"abcd\'"') # not tested + sage: css_escape(r'\'"abcd\'"') '---abcd---' - sage: css_escape('my-invalid/identifier') # not tested + sage: css_escape('my-invalid/identifier') 'my-invalid-identifier' - sage: css_escape(r'quotes"mustbe!escaped') # not tested + sage: css_escape(r'quotes"mustbe!escaped') 'quotes-mustbe-escaped' """ return css_illegal_re.sub('-', string) @@ -119,11 +100,11 @@ return ''.join([x if x.isalnum() else '_' for x in name]) env.filters['css_escape'] = css_escape -env.filters['number_of_rows'] = simplefilter(number_of_rows) -env.filters['clean_name'] = stringfilter(clean_name) -env.filters['prettify_time_ago'] = simplefilter(prettify_time_ago) -env.filters['math_parse'] = stringfilter(math_parse) -env.filters['max'] = simplefilter(max) +env.filters['number_of_rows'] = number_of_rows +env.filters['clean_name'] = clean_name +env.filters['prettify_time_ago'] = prettify_time_ago +env.filters['math_parse'] = math_parse +env.filters['max'] = max def template(filename, **user_context): """ @@ -139,7 +120,7 @@ the file's template variables OUTPUT: - + - a string - the rendered HTML, CSS, etc. EXAMPLES:: @@ -164,7 +145,7 @@ 'conf': notebook.conf() if notebook else None} try: tmpl = env.get_template(filename) - except jinja.exceptions.TemplateNotFound: + except jinja2.exceptions.TemplateNotFound: return "Notebook Bug -- missing template %s"%filename context = dict(default_context) context.update(user_context) diff --git a/sagenb/notebook/twist.py b/sagenb/notebook/twist.py --- a/sagenb/notebook/twist.py +++ b/sagenb/notebook/twist.py @@ -52,7 +52,7 @@ from sagenb.misc.misc import (SAGE_DOC, DATA, SAGE_VERSION, walltime, tmp_filename, tmp_dir, is_package_installed, - jsmath_macros) + jsmath_macros, encoded_str, unicode_str) css_path = os.path.join(DATA, "sage", "css") image_path = os.path.join(DATA, "sage", "images") @@ -82,7 +82,11 @@ SEP = '___S_A_G_E___' def encode_list(v): - return SEP.join([str(x) for x in v]) + seq = [] + for x in v: + x = encoded_str(x) + seq.append(x) + return SEP.join(seq) @@ -150,8 +154,7 @@ """ def __init__(self, code=None, headers=None, stream=None): if stream is not None: - if isinstance(stream, unicode): - stream = stream.encode('utf-8', 'ignore') + stream = encoded_str(stream) super(Response, self).__init__(code, headers, stream) def HTMLResponse(*args, **kwds): @@ -1092,9 +1095,10 @@ def render(self, ctx): id = self.id(ctx) if not ctx.args.has_key('input'): - input = '' + input = u'' else: input = ctx.args['input'][0] + input = unicode_str(input) cell = self.worksheet.new_cell_before(id, input=input) self.worksheet.increase_state_number() @@ -1111,6 +1115,7 @@ input = '' else: input = ctx.args['input'][0] + input = unicode_str(input) cell = self.worksheet.new_text_cell_before(id, input=input) s = encode_list([cell.id(), cell.html(editing=True), id]) @@ -1127,6 +1132,8 @@ input = '' else: input = ctx.args['input'][0] + input = unicode_str(input) + cell = self.worksheet.new_cell_after(id, input=input) s = encode_list([cell.id(), cell.html(div_wrap=False), id]) return HTMLResponse(stream = s) @@ -1141,6 +1148,7 @@ input = '' else: input = ctx.args['input'][0] + input = unicode_str(input) cell = self.worksheet.new_text_cell_after(id, input=input) s = encode_list([cell.id(), cell.html(editing=True), id]) @@ -1202,13 +1210,10 @@ if status == 'd': new_input = cell.changed_input_text() out_html = cell.output_html() - try: - H = "Worksheet '%s' (%s)\n"%(worksheet.name(), time.strftime("%Y-%m-%d at %H:%M",time.localtime(time.time()))) - H += cell.edit_text(ncols=HISTORY_NCOLS, prompts=False, - max_out=HISTORY_MAX_OUTPUT) - notebook.add_to_user_history(H, self.username) - except UnicodeDecodeError: - pass + H = "Worksheet '%s' (%s)\n"%(worksheet.name(), time.strftime("%Y-%m-%d at %H:%M",time.localtime(time.time()))) + H += cell.edit_text(ncols=HISTORY_NCOLS, prompts=False, + max_out=HISTORY_MAX_OUTPUT) + notebook.add_to_user_history(H, self.username) else: new_input = '' out_html = '' @@ -1254,10 +1259,11 @@ def render(self, ctx): id = self.id(ctx) if not ctx.args.has_key('input'): - input_text = '' + input_text = u'' else: input_text = ctx.args['input'][0] input_text = input_text.replace('\r\n', '\n') # DOS + input_text = unicode_str(input_text) W = self.worksheet W.increase_state_number() diff --git a/sagenb/notebook/worksheet.py b/sagenb/notebook/worksheet.py --- a/sagenb/notebook/worksheet.py +++ b/sagenb/notebook/worksheet.py @@ -31,7 +31,8 @@ alarm, cancel_alarm, verbose, DOT_SAGENB, walltime, ignore_nonexistent_files, set_restrictive_permissions, - set_permissive_permissions) + set_permissive_permissions, + encoded_str, unicode_str) from sagenb.misc.remote_file import get_remote_file @@ -271,19 +272,7 @@ sage: import sagenb.notebook.worksheet sage: W = sagenb.notebook.worksheet.Worksheet('test', 0, tmp_dir(), owner='sage') sage: sorted((W.basic().items())) - [('auto_publish', False), - ('collaborators', []), - ('id_number', 0), - ('last_change', ('sage', ...)), - ('name', 'test'), - ('owner', 'sage'), - ('pretty_print', False), - ('published_id_number', None), - ('ratings', []), - ('system', None), - ('tags', {'sage': [1]}), - ('viewers', []), - ('worksheet_that_was_published', ('sage', 0))] + [('auto_publish', False), ('collaborators', []), ('id_number', 0), ('last_change', ('sage', ...)), ('name', u'test'), ('owner', 'sage'), ('pretty_print', False), ('published_id_number', None), ('ratings', []), ('system', None), ('tags', {'sage': [1]}), ('viewers', []), ('worksheet_that_was_published', ('sage', 0))] """ try: published_id_number = int(os.path.split(self.__published_version)[1]) @@ -690,7 +679,7 @@ sage: nb = sagenb.notebook.notebook.Notebook(tmp_dir()+'.sagenb') sage: W = nb.create_new_worksheet('A Test Worksheet', 'admin') sage: W.name() - 'A Test Worksheet' + u'A Test Worksheet' """ try: return self.__name @@ -712,10 +701,11 @@ sage: W = nb.create_new_worksheet('A Test Worksheet', 'admin') sage: W.set_name('A renamed worksheet') sage: W.name() - 'A renamed worksheet' + u'A renamed worksheet' """ if len(name.strip()) == 0: - name = 'Untitled' + name = u'Untitled' + name = unicode_str(name) self.__name = name def set_filename_without_owner(self, nm): @@ -1913,8 +1903,8 @@ if os.path.exists(worksheet_html) and open(worksheet_html).read() == E: # we already wrote it out... return - open(filename, 'w').write(bz2.compress(E)) - open(worksheet_html, 'w').write(self.body()) + open(filename, 'w').write(bz2.compress(E.encode('utf-8', 'ignore'))) + open(worksheet_html, 'w').write(self.body().encode('utf-8', 'ignore')) self.limit_snapshots() try: X = self.__saved_by_info @@ -2141,7 +2131,7 @@ 5, Cell 1; in=2+8, out= 10] sage: W.name() - 'Test Edit Save' + u'Test Edit Save' """ # Clear any caching. try: @@ -3451,7 +3441,7 @@ """ # The extra newline below is necessary, since otherwise source # code introspection doesn't include the last line. - return 'open("%s","w").write("# -*- coding: utf-8 -*-\\n" + _support_.preparse_worksheet_cell(base64.b64decode("%s"),globals())+"\\n"); execfile(os.path.abspath("%s"))'%(CODE_PY, base64.b64encode(s), CODE_PY) + return 'open("%s","w").write("# -*- coding: utf-8 -*-\\n" + _support_.preparse_worksheet_cell(base64.b64decode("%s"),globals())+"\\n"); execfile(os.path.abspath("%s"))'%(CODE_PY, base64.b64encode(s.encode('utf-8', 'ignore')), CODE_PY) ########################################################## # Loading and attaching files @@ -3608,12 +3598,12 @@ sage: W.get_cell_system(c0) 'sage' sage: W.get_cell_system(c1) - 'gap' + u'gap' sage: W.edit_save('{{{\n%sage\n2+3\n}}}\n\n{{{\nSymmetricGroup(5)\n}}}') sage: W.set_system('gap') sage: c0, c1 = W.cell_list() sage: W.get_cell_system(c0) - 'sage' + u'sage' sage: W.get_cell_system(c1) 'gap' """ @@ -3638,7 +3628,7 @@ os.makedirs(code) spyx = os.path.abspath(os.path.join(code, 'sage%s.spyx'%id)) if not (os.path.exists(spyx) and open(spyx).read() == cmd): - open(spyx,'w').write(cmd) + open(spyx,'w').write(cmd.encode('utf-8', 'ignore')) return '_support_.cython_import_all("%s", globals())'%spyx def check_for_system_switching(self, input, cell): @@ -3665,9 +3655,9 @@ sage: W.edit_save('{{{\n2+3\n}}}\n\n{{{\n%gap\nSymmetricGroup(5)\n}}}') sage: c0, c1 = W.cell_list() sage: W.check_for_system_switching(c0.cleaned_input_text(), c0) - (False, '2+3') + (False, u'2+3') sage: W.check_for_system_switching(c1.cleaned_input_text(), c1) - (True, "print _support_.syseval(gap, ur'''SymmetricGroup(5)''', '...')") + (True, u"print _support_.syseval(gap, ur'''SymmetricGroup(5)''', '...')") :: @@ -3689,10 +3679,9 @@ sage: W.set_system('gap') sage: c0, c1 = W.cell_list() sage: W.check_for_system_switching(c0.cleaned_input_text(), c0) - (False, '2+3') + (False, u'2+3') sage: W.check_for_system_switching(c1.cleaned_input_text(), c1) - (True, - "print _support_.syseval(gap, ur'''SymmetricGroup(5)''', '...')") + (True, u"print _support_.syseval(gap, ur'''SymmetricGroup(5)''', '...')") sage: c0.evaluate() sage: W.check_comp() #random output -- depends on the computer's speed ('d', Cell 0; in=%sage diff --git a/sagenb/storage/filesystem_storage.py b/sagenb/storage/filesystem_storage.py --- a/sagenb/storage/filesystem_storage.py +++ b/sagenb/storage/filesystem_storage.py @@ -39,7 +39,7 @@ import os from abstract_storage import Datastore -from sagenb.misc.misc import set_restrictive_permissions +from sagenb.misc.misc import set_restrictive_permissions, encoded_str def is_safe(a): """ @@ -291,7 +291,7 @@ # only save if loaded # todo -- add check if changed filename = self._worksheet_html_filename(username, id_number) - open(self._abspath(filename),'w').write(worksheet.body()) + open(self._abspath(filename),'w').write(worksheet.body().encode('utf-8', 'ignore')) def load_worksheet(self, username, id_number): """ @@ -339,7 +339,7 @@ if title: # change the title basic['name'] = title - + basic['name'] = encoded_str(basic['name']) # Remove metainformation that perhaps shouldn't be distributed for k in ['owner', 'ratings', 'worksheet_that_was_published', 'viewers', 'tags', 'published_id_number', 'collaborators', 'auto_publish']: @@ -397,7 +397,7 @@ worksheet_txt = members[0].name W = self.load_worksheet(username, id_number) - W.edit_save_old_format(T.extractfile(worksheet_txt).read()) + W.edit_save_old_format(T.extractfile(worksheet_txt).read().decode('utf-8', 'ignore')) # '/' is right, since old worksheets always unix dir = worksheet_txt.split('/')[0] diff --git a/sagenb/todo.txt b/sagenb/todo.txt --- a/sagenb/todo.txt +++ b/sagenb/todo.txt @@ -7,9 +7,6 @@ sage: notebook() # then let the notebook start up, then hit ctrl-c sage: pwd --> '/Users/palmieri/.sage' -[ ] rate a worksheet -- note that the top of the browser (title) says -error, though in fact there is no error. - [ ] admin should *NOT* see all users worksheets. This is not good, e.g., when there are 50,000 worksheets! @@ -70,17 +67,9 @@ [ ] "untitled renaming". Make it an option in the user confs, which is on by default, by you can turn it off. -[ ] add message to the top of each *old* sage notebook .py file - stating that one should work on sagenb instead. This is a patch to - the core Sage library. - -[ ] fix all doctests in sagenb code and make a patch against core -sage library that imports enough of sagenb by default so I can -run all these doctests. [ ] get coverage to 100%. -[ ] change to 100% html format for worksheets. [ ] we can make it so that if worksheet.html file changes on disk but not currently open, then when reopen it uses the new version from @@ -109,7 +98,7 @@ [ ] Jonathan Gutow -- redo how jmol works -[ ] notebook docs -- something in "doc/en/sagenb" + [ ] maybe get rid of list of 'Sage Users' when publishing worksheets -- that's not appropriate given how sage has grown. @@ -139,7 +128,6 @@ [ ] ldap support (see, eg., http://math.univ-lyon1.fr/~tdumont/sage) but make sure to first abstract away authentication first so it is much CLEARER. -[ ] automated testing -- "Windmill seems to be more popular now -- especially with Python stuff " [ ] find a way to provide same info as data directory but without making it super-permissive. Maybe I can make an api for getting files from a data location or something... @@ -483,3 +471,19 @@ [x] When saving a worksheet, the default filename has "..." in it. +[x] add message to the top of each *old* sage notebook .py file + stating that one should work on sagenb instead. This is a patch to + the core Sage library. + +[x] fix all doctests in sagenb code and make a patch against core +sage library that imports enough of sagenb by default so I can +run all these doctests. + +[x] rate a worksheet -- note that the top of the browser (title) says +error, though in fact there is no error. + +[x] change to 100% html format for worksheets. + +[x] notebook docs -- something in "doc/en/sagenb" + +[x] automated testing -- "Windmill seems to be more popular now -- especially with Python stuff " diff --git a/sass/src/_topbar.sass b/sass/src/_topbar.sass --- a/sass/src/_topbar.sass +++ b/sass/src/_topbar.sass @@ -87,7 +87,7 @@ div - &#user-controls + &#user-main-controls +clearfix :clear both :border-bottom 1px solid #c9d7f1 diff --git a/sass/src/_worksheet_listing.sass b/sass/src/_worksheet_listing.sass new file mode 100644 --- /dev/null +++ b/sass/src/_worksheet_listing.sass @@ -0,0 +1,268 @@ +/************ User Home (Worksheet listing) **************************/ + +#worksheet-listing-page + #welcome-message + :text-align center + :padding 1em + +.ratingmsg + :color #112abb + :padding 0.3em + :font-size 14px + +.pubmsg + :font-family sans-serif + :color #112abb + :padding 0.3em + :font-size 12px + +#worksheet-list + :clear both + :width 100% + thead + :background-color #e8eef7 + td.checkbox + :padding 4px + +.controls a, .usercontrol + :color #112abb + :font-size 14px + :text-decoration underline + + &:hover + :cursor pointer + +.controls + span + :color #112abb + :padding 0.3em + :font-size 14px + + +.user-controls a, .boldusercontrol + :color #112abb + :font-weight bold + :font-size 14px + +.user-controls a, .controls a, .controls span + :padding 0.3em + +a + &.control, &.control-select + :background-color #7799bb + :font-family sans-serif + :color #ffffff + :padding-top 0.25em + :padding-bottom 0.25em + :padding-left 0.5em + :padding-right 0.5em + :font-size 15px + :font-weight bold + :text-decoration none + + &.control:hover + :cursor pointer + + &.control-select + :background-color #4477aa + + &:hover + :cursor pointer + + +.sharebar + :background-color #4477aa + :font-family sans-serif + :color #ffffff + :padding-top 0.5em + :padding-bottom 0.5em + :padding-left 2em + :font-size 1.25em + :font-weight bold + + +textarea.edit + :font-family courier, monospace + :font-size 10pt + :border 1px solid #8cacbb + :color black + :background-color white + :padding 3px + :overflow auto + :margin-top 0.5em + + +a.listcontrol + :padding 1ex + :color #112abb + :font-weight bold + :font-size 14px + :text-decoration none + + +hr + &.usercontrol + :border 0 + :width 99% + :color #c9d7f1 + :background-color #c9d7f1 + :height 1px + + &.greybar hr.negative_greybar + :border 0 + :width 99% + :color #aaa + :background-color #aaa + :height 1px + + &.negative_greybar + :top -1em + :position relative + + +span + &.checkcol + :position relative + :left 0% + :width 10% + + &.leftcol + :position relative + :left 10% + :width 20% + + &.middlecol + :position relative + :left 30% + :width 20% + + &.rightcol + :position relative + :left 50% + :width 20% + + +tr.greybox + :background-color #e8eef7 + + +td.entry + :padding 4px + + +div.thinspace + :border 0 + :height 2px + + +tr.thingreybox + :background-color #aaa + + +div.ultrathinspace + :border 0 + :height 0px + + +.lastedit + :font-family sans-serif + :font-size 10px + :color #717171 + +.revs + :font-family sans-serif + :font-size 12px + :font-weight bold + :color #333333 + +.users + :font-family sans-serif + :font-size 13px + :color #222222 + + +a.share + :font-family sans-serif + :font-size 10px + :color #7777cc + + +select + &.worksheet + :width 6em + :border #aaaaaa + :border-style solid + :border-top-width 1px + :border-right-width 1px + :border-bottom-width 1px + :border-left-width 1px + + &.worksheet_list, &.worksheet_edit + :width 5em + :border #aaaaaa + :border-style solid + :border-top-width 1px + :border-right-width 1px + :border-bottom-width 1px + :border-left-width 1px + + +td + &.worksheet_link + :font-family sans-serif + :font-size 12px + :font-weight bold + :color #000000 + + &.archived_worksheet_link, &.owner_collab, &.last_edited + :font-family sans-serif + :font-size 12px + :color #000000 + + +span.addtext + :font-family sans-serif + :font-size 13px + :color #222 + + +textarea.plaintextedit + :font-family courier, monospace + :font-size 10pt + :border 1px solid #8cacbb + :color black + :background-color white + :overflow auto + :width 99% + :height 60% + + +pre.plaintext + :overflow auto + :font-family courier, monospace + :font-size 10pt + :border 1px solid #8cacbb + :color black + :background-color white + :margin-top 0.5em + + +div.docidx + :text-align center + :font-family sans-serif + :font-size 16px + :color #222 + :font-weight bold + + +span + &.ping + :display none + + &.pingdown + :font-family sans-serif + :font-size 15px + :font-weight bold + :color white + :background-color #990000 + :margin-left 1em diff --git a/sass/src/main.sass b/sass/src/main.sass --- a/sass/src/main.sass +++ b/sass/src/main.sass @@ -1184,7 +1184,7 @@ :position relative :top -20ex :right 0ex - +t .thin-right :position absolute @@ -1192,270 +1192,7 @@ :right 0 :width 70% -/************ User Home (Worksheet listing) **************************/ - -.ratingmsg - :color #112abb - :padding 0.3em - :font-size 14px - -.pubmsg - :font-family sans-serif - :color #112abb - :padding 0.3em - :font-size 12px - -#worksheet-list - :clear both - :width 100% - thead - :background-color #e8eef7 - td.checkbox - :padding 4px - -.controls a, .usercontrol - :color #112abb - :font-size 14px - :text-decoration underline - - &:hover - :cursor pointer - -.controls - span - :color #112abb - :padding 0.3em - :font-size 14px - - -.user-controls a, .boldusercontrol - :color #112abb - :font-weight bold - :font-size 14px - -.user-controls a, .controls a, .controls span - :padding 0.3em - -a - &.control, &.control-select - :background-color #7799bb - :font-family sans-serif - :color #ffffff - :padding-top 0.25em - :padding-bottom 0.25em - :padding-left 0.5em - :padding-right 0.5em - :font-size 15px - :font-weight bold - :text-decoration none - - &.control:hover - :cursor pointer - - &.control-select - :background-color #4477aa - - &:hover - :cursor pointer - - -.sharebar - :background-color #4477aa - :font-family sans-serif - :color #ffffff - :padding-top 0.5em - :padding-bottom 0.5em - :padding-left 2em - :font-size 1.25em - :font-weight bold - - -textarea.edit - :font-family courier, monospace - :font-size 10pt - :border 1px solid #8cacbb - :color black - :background-color white - :padding 3px - :overflow auto - :margin-top 0.5em - - -a.listcontrol - :padding 1ex - :color #112abb - :font-weight bold - :font-size 14px - :text-decoration none - - -hr - &.usercontrol - :border 0 - :width 99% - :color #c9d7f1 - :background-color #c9d7f1 - :height 1px - - &.greybar hr.negative_greybar - :border 0 - :width 99% - :color #aaa - :background-color #aaa - :height 1px - - &.negative_greybar - :top -1em - :position relative - - -span - &.checkcol - :position relative - :left 0% - :width 10% - - &.leftcol - :position relative - :left 10% - :width 20% - - &.middlecol - :position relative - :left 30% - :width 20% - - &.rightcol - :position relative - :left 50% - :width 20% - - -tr.greybox - :background-color #e8eef7 - - -td.entry - :padding 4px - - -div.thinspace - :border 0 - :height 2px - - -tr.thingreybox - :background-color #aaa - - -div.ultrathinspace - :border 0 - :height 0px - - -.lastedit - :font-family sans-serif - :font-size 10px - :color #717171 - -.revs - :font-family sans-serif - :font-size 12px - :font-weight bold - :color #333333 - -.users - :font-family sans-serif - :font-size 13px - :color #222222 - - -a.share - :font-family sans-serif - :font-size 10px - :color #7777cc - - -select - &.worksheet - :width 6em - :border #aaaaaa - :border-style solid - :border-top-width 1px - :border-right-width 1px - :border-bottom-width 1px - :border-left-width 1px - - &.worksheet_list, &.worksheet_edit - :width 5em - :border #aaaaaa - :border-style solid - :border-top-width 1px - :border-right-width 1px - :border-bottom-width 1px - :border-left-width 1px - - -td - &.worksheet_link - :font-family sans-serif - :font-size 12px - :font-weight bold - :color #000000 - - &.archived_worksheet_link, &.owner_collab, &.last_edited - :font-family sans-serif - :font-size 12px - :color #000000 - - -span.addtext - :font-family sans-serif - :font-size 13px - :color #222 - - -textarea.plaintextedit - :font-family courier, monospace - :font-size 10pt - :border 1px solid #8cacbb - :color black - :background-color white - :overflow auto - :width 99% - :height 60% - - -pre.plaintext - :overflow auto - :font-family courier, monospace - :font-size 10pt - :border 1px solid #8cacbb - :color black - :background-color white - :margin-top 0.5em - - -div.docidx - :text-align center - :font-family sans-serif - :font-size 16px - :color #222 - :font-weight bold - - -span - &.ping - :display none - - &.pingdown - :font-family sans-serif - :font-size 15px - :font-weight bold - :color white - :background-color #990000 - :margin-left 1em - +@import worksheet_listing.sass @import prettify.sass @import source.sass @import print_worksheet.sass