1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
|
##
# \file cadabra2_defaults.py
# \ingroup pythoncore
# Cadabra2 pure Python functionality.
#
# This is a pure-python initialisation script to set the path to
# sympy and setup printing of Cadabra expressions. This script is
# called both by the command line interface 'cadabra2' as well as by
# the GUI backend server 'cadabra-server'.
import sys
import cadabra2
from cadabra2 import *
from importlib.machinery import PathFinder, ModuleSpec, SourceFileLoader
from importlib.abc import MetaPathFinder
from cdb_appdirs import user_config_dir, user_data_dir
import datetime
import atexit
import rlcompleter
__cdbkernel__=cadabra2.__cdbkernel__
__cdbkernel__.completer=rlcompleter.Completer()
import os
os.environ.setdefault('PATH', '')
PY3 = sys.version_info[0] == 3
if PY3:
unicode = str
if "@ENABLE_JUPYTER@" == "OFF":
discr = "\\discretionary{}{}{} "
else:
discr = ""
class PackageCompiler(MetaPathFinder):
@classmethod
def find_module(cls, fullname, path):
return find_spec(fullname, path, None)
@classmethod
def find_spec(cls, fullname, path, target=None):
#log_("Finding {}, path=[{}]".format(fullname, ', '.join(f'"{p}"' for p in path) if path is not None else ""))
# Top-level import if path=None
if path is None or path == "":
path = sys.path
# Get unqualified package name
if '.' in fullname:
parents = fullname.split('.')
name = parents.pop()
else:
name = fullname
parents = []
# Go through path and try to find a notebook.
for entry in path:
have_cnb = os.path.isfile(os.path.join(entry, name + ".cnb"))
have_cdb = os.path.isfile(os.path.join(entry, name + ".cdb"))
have_ipynb = os.path.isfile(os.path.join(entry, name + ".ipynb"))
if have_cnb or have_cdb or have_ipynb:
# Notebook was found. Create a version of it in the temporary directory, then
# return a ModuleSpec object to allow Python to load that file
pkg_path = os.path.join(user_config_dir(), "cadabra_packages", *parents)
# Create the path if it doesn't exist
if not os.path.exists(pkg_path):
os.makedirs(pkg_path)
if have_cnb:
compile_package__(os.path.join(entry, name + ".cnb"), os.path.join(pkg_path, name + ".py"))
elif have_cdb:
compile_package__(os.path.join(entry, name + ".cdb"), os.path.join(pkg_path, name + ".py"))
else:
compile_package__(os.path.join(entry, name + ".ipynb"), os.path.join(pkg_path, name + ".py"))
return ModuleSpec(
fullname,
SourceFileLoader(fullname, os.path.join(pkg_path, name + ".py")),
origin=os.path.join(pkg_path, name + ".py"))
# Return none if no notebook was found
return None
# Prepend to sys.meta_path, so that all imports will first be checked in
# case they are notebooks that need compiling
sys.meta_path.insert(0, PackageCompiler)
# Add current directory to Python module import path.
sys.path.append(".")
#sys.path.insert(0,'/home/kasper/Development/git.others/sympy')
# Attempt to import sympy; if not, setup logic so that the
# shell does not fail later.
try:
import sympy
except:
class Sympy:
"""!@brief Stub object for when Sympy itself is not available.
@long When Sympy is not available, this object contains some basic
functionality to prevent things from breaking elsewhere.
"""
__version__="unavailable"
sympy = Sympy()
if sympy.__version__ != "unavailable":
# from sympy import factor
# from sympy import integrate
# from sympy import diff
from sympy import symbols
from sympy import latex
# from sympy import sin, cos, tan, sqrt, trigsimp
from sympy import Matrix as sMatrix
# Whether running in command-line mode or as client-server, there always
# needs to be a Server object known as 'server' through which interaction
# with the display routines is handled. The 'display' function will
# call the 'server.send' method.
if 'server' in globals():
mopen="\\begin{dmath*}{}";
mclose="\\end{dmath*}";
else:
mopen=''
mclose=''
class Server:
"""!@brief Object to handle advanced display in a UI-independent way.
@long Cadabra makes available to Python a Server object, which
contains functions to send output to the user. When running
from the command line this simply prints to the screen, but it
can talk to a remote client to display images and maths.
"""
def send(self, data, typestr, parent_id, last_in_sequence):
""" Send a message to the client; 'typestr' indicates the cell type,
'parent_id', if non-null, indicates the serial number of the parent
cell.
"""
print(data)
return 0
def architecture(self):
return "terminal"
def test(self):
print("hello there!")
def handles(self, otype):
if(otype=="plain"):
return True
return False
def totals(self):
return __cdb_progress_monitor__.totals()
server = Server()
# Import matplotlib and setup functions to prepare its output
# for sending as base64 to the client. Example use:
#
# import matplotlib.pyplot as plt
# p = plt.plot([1,2,3],[1,2,5],'-o')
# display(p[0])
#
have_matplotlib=True
try:
import matplotlib
import matplotlib.artist
import matplotlib.figure
matplotlib.use('Agg')
except ImportError:
have_matplotlib=False
def save_history(history_path):
try:
readline.write_history_file(history_path)
except:
pass
try:
import readline
if not hasattr(readline, 'redisplay'):
readline.redisplay = lambda: None
history_path = os.path.join(user_data_dir(), "cadabra_history")
if os.path.exists(history_path):
readline.read_history_file(history_path)
readline.set_history_length(1000)
atexit.register(save_history, history_path)
except:
pass
import io
import base64
## @brief Generic display function which handles local as well as remote clients.
#
# The 'display' function is a replacement for 'str', in the sense that
# it will generate human-readable output. However, in contrast to
# 'str', it knows about what the front-end ('server') can display, and
# will adapt the output to that. For instance, if
# server.handles('latex_view') is true, it will generate LaTeX output,
# while it will generate just plain text otherwise.
#
# Once it has figured out which display is accepted by 'server', it
# will call server.send() with data depending on the object type it is
# being fed. Data types the server object can support are:
#
# - "latex_view": text-mode LaTeX string.
# - "image_png": base64 encoded png image.
# - "verbatim": ascii string to be displayed verbatim.
def display(obj, delay_send=False):
"""
Generalised 'print' function which knows how to display objects in the
best possible way on the used interface, be it a console or graphical
notebook. In particular, it knows how to display Cadabra expressions
in typeset form whenever LaTeX functionality is available. Can also be
used to display matplotlib plots.
When using a Cadabra front-end (command line or notebook), an expression
with a trailing semi-colon ';' will automatically be wrapped in a
'display' function call so that the expression is displayed immediately.
"""
if 'matplotlib' in sys.modules and isinstance(obj, matplotlib.figure.Figure):
imgstring = io.BytesIO()
obj.savefig(imgstring,format='png')
imgstring.seek(0)
b64 = base64.b64encode(imgstring.getvalue())
server.send(b64, "image_png", 0, False)
# FIXME: Use the 'handles' query method on the Server object
# to figure out whether it can do something useful
# with a particular data type.
elif 'matplotlib' in sys.modules and isinstance(obj, matplotlib.artist.Artist):
f = obj.get_figure()
imgstring = io.BytesIO()
f.savefig(imgstring,format='png')
imgstring.seek(0)
b64 = base64.b64encode(imgstring.getvalue())
server.send(b64, "image_png", 0, False)
elif hasattr(obj,'_backend'):
if hasattr(obj._backend,'fig'):
f = obj._backend.fig
imgstring = io.BytesIO()
f.savefig(imgstring,format='png')
imgstring.seek(0)
b64 = base64.b64encode(imgstring.getvalue())
server.send(b64, "image_png", 0, False)
elif 'vtk' in sys.modules and isinstance(obj, vtk.vtkRenderer):
# Vtk renderer, see http://nbviewer.ipython.org/urls/bitbucket.org/somada141/pyscience/raw/master/20140917_RayTracing/Material/PythonRayTracingEarthSun.ipynb
pass
# elif isinstance(obj, numpy.ndarray):
# server.send("\\begin{dmath*}{}"+str(obj.to_list())+"\\end{dmath*}", "latex")
elif isinstance(obj, Ex):
if server.handles('latex_view'):
if delay_send:
return obj._latex_()
else:
ret = mopen+obj._latex_()+mclose
id=server.send(ret, "latex_view", 0, False)
# print(id)
# Make a child cell of the above with input form content.
server.send(obj.input_form(), "input_form", id, False)
else:
server.send(unicode(obj), "plain", 0, False)
elif isinstance(obj, Property):
if server.handles('latex_view'):
ret = mopen+obj._latex_()+mclose
if delay_send:
return ret
else:
server.send(ret , "latex_view", 0, False)
# Not yet available.
# server.send(obj.input_form(), "input_form", 0, False)
else:
server.send(unicode(obj), "plain", 0, False)
elif type(obj)==list:
out="{}$\\big[$"
first=True
for elm in obj:
if first==False:
out+=","+discr
else:
first=False
nxt = display(elm, True)
if nxt != None:
out+= "$"+display(elm, True)+"$"
out+="$\\big]$";
server.send(out, "latex_view", 0, False)
# FIXME: send input_form version.
elif hasattr(obj, "__module__") and hasattr(obj.__module__, "find") and obj.__module__.find("sympy")!=-1:
if delay_send:
return latex(obj)
else:
server.send("\\begin{dmath*}{}"+latex(obj)+"\\end{dmath*}", "latex_view", 0, False)
else:
# Failing all else, just dump a str representation to the notebook, asking
# it to display this verbatim.
# server.send("\\begin{dmath*}{}"+str(obj)+"\\end{dmath*}", "latex")
if delay_send:
return "\\verb|"+str(obj)+"|"
else:
server.send(unicode(obj), "verbatim", 0, False)
__cdbkernel__.server=server
__cdbkernel__.display=display
class Console(object):
"""
The interactive console works in the same Python context as
the notebook cells to allow evaluation of expressions for
debugging/logging purposes
"""
def log(self, *objs):
"""Sends a string representation of objs to the console"""
if server.architecture() == "terminal":
print(*objs)
elif server.architecture() == "client-server":
server.send(" ".join(str(obj) for obj in objs), "csl_out", 0, False)
def clear(self):
"""Clears the output of the console window"""
if server.architecture() == "client-server":
server.send("", "csl_clear", 0, False)
def warn(self, msg):
if server.architecture() == "terminal":
print("Warning: " + msg)
elif server.architecture() == "client-server":
server.send(msg, "csl_warn", 0, False)
console = Console()
__cdbkernel__.configure_warnings(callback=console.warn)
# Set display hooks to catch certain objects and print them
# differently. Should probably eventually be done cleaner.
def _displayhook(arg):
global remember_display_hook
if isinstance(arg, Ex):
print(unicode(arg))
elif isinstance(arg, Property):
print(unicode(arg))
else:
remember_display_hook(arg)
remember_display_hook = sys.displayhook
sys.displayhook = _displayhook
# Default post-processing algorithms. These are not pre-processed
# so need to have the '__cdbkernel__' argument.
def post_process(__cdbkernel__, ex):
collect_terms(ex)
|