# -*- coding: utf-8 -*-
"""
Miscellaneous Notebook Functions
TESTS:
Check that github issue #195 is fixed::
sage: from sagenb.misc.misc import mathjax_macros
sage: type(mathjax_macros)
<type 'list'>
"""
#############################################################################
# Copyright (C) 2006, 2007 William Stein <wstein@gmail.com>
# Distributed under the terms of the GNU General Public License (GPL)
# The full text of the GPL is available at:
# http://www.gnu.org/licenses/
#############################################################################
import sys
from six import text_type, binary_type
from pkg_resources import resource_filename
PYTHON_VERSION = sys.version[0]
if PYTHON_VERSION == '3':
CRE = ConnectionRefusedError
else:
CRE = tuple
[docs]def stub(f):
def g(*args, **kwds):
print("Stub: {}".format(f.func_name))
return f(*args, **kwds)
return g
min_password_length = 6
import os
import socket
import sys
try:
import cPickle as pickle
except ImportError:
import pickle
[docs]def print_open_msg(address, port, secure=False, path=""):
"""
Print a message on the screen suggesting that the user open their
web browser to a certain URL.
INPUT:
- ``address`` -- a string; a computer address or name
- ``port`` -- an int; a port number
- ``secure`` -- a bool (default: False); whether to prefix the URL
with 'http' or 'https'
- ``path`` -- a string; the URL's path following the port.
EXAMPLES::
sage: from sagenb.misc.misc import print_open_msg
sage: print_open_msg('localhost', 8080, True)
┌──────────────────────────────────────────────────┐
│ │
│ Open your web browser to https://localhost:8080 │
│ │
└──────────────────────────────────────────────────┘
sage: print_open_msg('sagemath.org', 8080, False)
┌────────────────────────────────────────────────────┐
│ │
│ Open your web browser to http://sagemath.org:8080 │
│ │
└────────────────────────────────────────────────────┘
sage: print_open_msg('sagemath.org', 90, False)
┌──────────────────────────────────────────────────┐
│ │
│ Open your web browser to http://sagemath.org:90 │
│ │
└──────────────────────────────────────────────────┘
sage: print_open_msg('sagemath.org', 80, False)
┌────────────────────────────────────────────────┐
│ │
│ Open your web browser to http://sagemath.org │
│ │
└────────────────────────────────────────────────┘
"""
if port == 80:
port = ''
else:
port = ':%s'%port
s = "Open your web browser to http%s://%s%s%s"%('s' if secure else '', address, port, path)
t = len(s)
if t%2:
t += 1
s += ' '
n = max(t+4, 50)
k = n - t - 1
j = k // 2
msg = '┌' + '─' * (n - 2) + '┐\n'
msg += '│' + ' ' * (n - 2) + '│\n'
msg += '│' + ' ' * j + s + ' ' * j + '│\n'
msg += '│' + ' ' * (n - 2) + '│\n'
msg += '└' + '─' * (n - 2) + '┘'
print(msg)
[docs]def find_next_available_port(interface, start, max_tries=100, verbose=False):
"""
Find the next available port at a given interface, that is, a port for
which a current connection attempt returns a 'Connection refused'
error message. If no port is found, raise a RuntimeError exception.
INPUT:
- ``interface`` - address to check
- ``start`` - an int; the starting port number for the scan
- ``max_tries`` - an int (default: 100); how many ports to scan
- ``verbose`` - a bool (default: True); whether to print information
about the scan
OUTPUT:
- an int - the port number
EXAMPLES::
sage: from sagenb.misc.misc import find_next_available_port
sage: find_next_available_port('127.0.0.1', 9000, verbose=False) # random output -- depends on network
9002
"""
from signal import alarm
alarm_count = 0
for port in range(start, start+max_tries+1):
try:
try:
alarm(5)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((interface, port))
finally:
alarm(0) # cancel alarm
except socket.error as msg:
if ((PYTHON_VERSION == '2' and msg[1] == 'Connection refused')
or (PYTHON_VERSION == '3' and isinstance(msg, CRE))):
if verbose:
print("Using port = %s" % port)
return port
except KeyboardInterrupt:
if verbose:
print("alarm")
alarm_count += 1
if alarm_count >= 10:
break
if verbose:
print("Port %s is already in use." % port)
print("Trying next port...")
raise RuntimeError("no available port.")
[docs]def open_page(address, port, secure, path=""):
if secure:
rsrc = 'https'
else:
rsrc = 'http'
os.system('%s %s://%s:%s%s 1>&2 > /dev/null &'%(browser(), rsrc, address, port, path))
[docs]def pad_zeros(s, size=3):
"""
EXAMPLES::
sage: pad_zeros(100)
'100'
sage: pad_zeros(10)
'010'
sage: pad_zeros(10, 5)
'00010'
sage: pad_zeros(389, 5)
'00389'
sage: pad_zeros(389, 10)
'0000000389'
"""
return "0"*(size-len(str(s))) + str(s)
SAGENB_ROOT = os.path.split(resource_filename(__name__, ''))[0]
DATA = os.path.join(SAGENB_ROOT, 'data')
if 'DOT_SAGENB' in os.environ:
DOT_SAGENB = os.environ['DOT_SAGENB']
elif 'DOT_SAGE' in os.environ:
DOT_SAGENB = os.environ['DOT_SAGE']
else:
DOT_SAGENB = os.path.join(os.environ['HOME'], '.sagenb')
try:
from sage.env import SAGE_URL
except ImportError:
SAGE_URL = 'http://sagemath.org'
# TODO: Get macros from server and user settings.
try:
import sage.all
from sage.misc.latex_macros import sage_mathjax_macros
mathjax_macros = sage_mathjax_macros()
except ImportError:
mathjax_macros = [
"ZZ : '{\\\\Bold{Z}}'",
"RR : '{\\\\Bold{R}}'",
"CC : '{\\\\Bold{C}}'",
"QQ : '{\\\\Bold{Q}}'",
"QQbar : '{\\\\overline{\\\\QQ}}'",
"GF : ['{\\\\Bold{F}_{#1}}', 1]",
"Zp : ['{\\\\ZZ_{#1}}', 1]",
"Qp : ['{\\\\QQ_{#1}}', 1]",
"Zmod : ['{\\\\ZZ/#1\\\\ZZ}', 1]",
"CIF : '{\\\\Bold{C}}'",
"CLF : '{\\\\Bold{C}}'",
"RDF : '{\\\\Bold{R}}'",
"RIF : '{\\\\Bold{I} \\\\Bold{R}}'",
"RLF : '{\\\\Bold{R}}'",
"CFF : '{\\\\Bold{CFF}}'",
"Bold : ['{\\\\mathbf{#1}}', 1]"
]
except Exception:
sage_mathjax_macros_easy = []
raise
try:
from sage.misc.session import init as session_init
except ImportError:
@stub
def session_init(*args, **kwds):
pass
try:
from sage.misc.sage_eval import sage_eval
except ImportError:
def sage_eval(value, globs):
# worry about ^ and preparser -- this gets used in interact.py,
# which is a bit weird, but heh.
return eval(value, globs)
try:
from sage.misc.package import is_package_installed
except ImportError:
def is_package_installed(name, *args, **kwds):
return False
try:
from sage.misc.viewer import browser
except ImportError:
@stub
def browser():
return "open"
try:
from sage.structure.sage_object import loads, dumps, load, save
except ImportError:
loads = pickle.loads
dumps = pickle.dumps
def load(filename):
return pickle.loads(open(filename).read())
def save(obj, filename):
s = pickle.dumps(obj, protocol=2)
open(filename, 'wb').write(s)
try:
from sage.misc.all import verbose
except ImportError:
# TODO!
@stub
def verbose(*args, **kwds):
pass
################################
# clocks -- easy to implement
################################
import time, resource
[docs]def cputime(t=0):
try:
t = float(t)
except TypeError:
t = 0.0
u, s = resource.getrusage(resource.RUSAGE_SELF)[:2]
return u + s - t
[docs]def walltime(t=0):
return time.time() - t
[docs]def word_wrap(s, ncols=85):
t = []
if ncols == 0:
return s
for x in s.split('\n'):
if len(x) == 0 or x.lstrip()[:5] == 'sage:':
t.append(x)
continue
while len(x) > ncols:
k = ncols
while k > 0 and x[k] != ' ':
k -= 1
if k == 0:
k = ncols
end = '\\'
else:
end = ''
t.append(x[:k] + end)
x = x[k:]
k=0
while k < len(x) and x[k] == ' ':
k += 1
x = x[k:]
t.append(x)
return '\n'.join(t)
try:
from sage.repl.preparse import strip_string_literals
except ImportError:
def strip_string_literals(code, state=None):
# todo -- do we need this?
return code
try:
from pkg_resources import Requirement, working_set
SAGENB_VERSION = working_set.find(Requirement.parse('sagenb')).version
except AttributeError:
SAGENB_VERSION = ""
try:
import sage.version
SAGE_VERSION=sage.version.version
except ImportError:
SAGE_VERSION=""
try:
from sage.plot.colors import Color
except ImportError:
class Color:
def __init__(self, *args, **kwds):
pass
########################################
# this is needed for @interact:
# Color, sage_eval and is_Matrix
# are imported from here in notebook/interact.py
########################################
[docs]def is_Matrix(x):
try:
from sage.structure.element import is_Matrix
except ImportError:
return False
return is_Matrix(x)
[docs]def register_with_cleaner(pid):
try:
import sage.interfaces.cleaner
sage.interfaces.cleaner.cleaner(pid) # register pid of forked process with cleaner
except ImportError:
print("generic cleaner needs to be written")
try:
from sage.misc.all import tmp_filename, tmp_dir
except ImportError:
def tmp_filename(name='tmp'):
# We use mktemp instead of mkstemp since the semantics of the
# tmp_filename function simply don't allow for what mkstemp
# provides.
import tempfile
return tempfile.mktemp()
def tmp_dir(name='dir'):
import tempfile
return tempfile.mkdtemp()
try:
from sage.misc.inline_fortran import InlineFortran
except ImportError:
@stub
def InlineFortran(*args, **kwds):
pass
try:
from sage.misc.cython import cython
except ImportError:
[docs] @stub
def cython(*args, **kwds):
# TODO
raise NotImplementedError("Curently %cython mode requires Sage.")
#############################################################
# File permissions
# May need some changes on Windows.
#############################################################
import stat
[docs]def set_restrictive_permissions(filename, allow_execute=False):
x = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
if allow_execute:
x = x | stat.S_IXGRP | stat.S_IXOTH
os.chmod(filename, x)
[docs]def set_permissive_permissions(filename):
os.chmod(filename, stat.S_IROTH | stat.S_IWOTH | stat.S_IXOTH | \
stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | \
stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP)
[docs]def encoded_str(obj, encoding='utf-8'):
r"""
Takes an object and returns an encoded str human-readable representation.
string to bytes
EXAMPLES::
sage: from sagenb.misc.misc import encoded_str
sage: encoded_str(u'\u011b\u0161\u010d\u0159\u017e\xfd\xe1\xed\xe9\u010f\u010e') == 'ěščřžýáíéďĎ'
True
sage: encoded_str(u'abc')
'abc'
sage: encoded_str(123)
'123'
"""
if isinstance(obj, text_type):
return obj.encode(encoding, 'ignore')
return binary_type(obj)
[docs]def unicode_str(obj, encoding='utf-8'):
r"""
Takes an object and returns a unicode human-readable representation.
(bytes or string) to string
EXAMPLES::
sage: from sagenb.misc.misc import unicode_str
sage: unicode_str('ěščřžýáíéďĎ') == u'\u011b\u0161\u010d\u0159\u017e\xfd\xe1\xed\xe9\u010f\u010e'
True
sage: unicode_str('abc')
u'abc'
sage: unicode_str(123)
u'123'
"""
if isinstance(obj, binary_type):
return obj.decode(encoding, 'ignore')
elif isinstance(obj, text_type):
return obj
return text_type(obj)
[docs]def ignore_nonexistent_files(curdir, dirlist):
"""
Returns a list of non-existent files, given a directory and its
contents. The returned list includes broken symbolic links. Use
this, e.g., with :func:`shutil.copytree`, as shown below.
INPUT:
- ``curdir`` - a string; the name of the current directory
- ``dirlist`` - a list of strings; names of ``curdir``'s contents
OUTPUT:
- a list of strings; names of ``curdir``'s non-existent files
EXAMPLES::
sage: import os, shutil
sage: from sagenb.misc.misc import ignore_nonexistent_files
sage: opj = os.path.join; ope = os.path.exists; t = tmp_dir()
sage: s = opj(t, 'src'); t = opj(t, 'trg'); hi = opj(s, 'hi.txt');
sage: os.makedirs(s)
sage: f = open(hi, 'w'); f.write('hi'); f.close()
sage: os.symlink(hi, opj(s, 'good.txt'))
sage: os.symlink(opj(s, 'bad'), opj(s, 'bad.txt'))
sage: slist = sorted(os.listdir(s)); slist
['bad.txt', 'good.txt', 'hi.txt']
sage: [ope(opj(s, x)) for x in slist]
[False, True, True]
sage: [os.path.islink(opj(s, x)) for x in slist]
[True, True, False]
sage: shutil.copytree(s, t)
Traceback (most recent call last):
...
Error: [('.../src/bad.txt', '.../trg/bad.txt', "[Errno 2] No such file or directory: '.../src/bad.txt'")]
sage: shutil.rmtree(t); ope(t)
False
sage: shutil.copytree(s, t, ignore = ignore_nonexistent_files)
sage: tlist = sorted(os.listdir(t)); tlist
['good.txt', 'hi.txt']
sage: [ope(opj(t, x)) for x in tlist]
[True, True]
sage: [os.path.islink(opj(t, x)) for x in tlist] # Note!
[False, False]
"""
ignore = []
for x in dirlist:
if not os.path.exists(os.path.join(curdir, x)):
ignore.append(x)
return ignore
[docs]def translations_path():
return os.path.join(SAGENB_ROOT, 'translations')
[docs]def get_languages():
from babel import Locale
from babel.core import UnknownLocaleError
langs = []
dir_names = [lang for lang in os.listdir(translations_path())
if lang != 'en_US']
for name in dir_names:
try:
Locale.parse(name)
except UnknownLocaleError:
pass
else:
langs.append(name)
langs.sort()
langs.insert(0, 'en_US')
return langs
[docs]def N_(message):
return message
[docs]def nN_(message_singular, message_plural):
return [message_singular, message_plural]