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
|
# Regina - A Normal Surface Theory Calculator
# Python Module Initialisation
#
# Copyright (c) 2003-2025, Ben Burton
# For further details contact Ben Burton (bab@debian.org).
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# As an exception, when this program is distributed through (i) the
# App Store by Apple Inc.; (ii) the Mac App Store by Apple Inc.; or
# (iii) Google Play by Google Inc., then that store may impose any
# digital rights management, device limits and/or redistribution
# restrictions that are required by its terms of service.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
import sys, os
from . import engine
from .engine import *
if 'sage' in sys.modules:
# Typing "from regina import *" should not override certain
# names. This always applies to the built-in "open".
#
# Also: the Sage preparser adds "Integer" and relies on the name
# being bound to a Sage integer. If we override it, Sage
# breaks in multiple ways.
#
# We skip names_to_avoid in __all__.
names_to_avoid = set(['open', 'Integer'])
__all__ = (
[ name for name in engine.__dict__.keys()
if not name in names_to_avoid and not name.startswith('_') ] +
[ 'reginaSetup' ])
# All additional sage-related setup should be placed within sageSetup.py.
from . import sageSetup
del sageSetup
else:
# Typing "from regina import *" is not supposed to override the
# built-in "open". To achieve this, we skip it in __all__.
__all__ = (
[ name for name in engine.__dict__.keys()
if name != 'open' and not name.startswith('_') ] +
[ 'reginaSetup' ])
def reginaSetup(quiet = False, readline = True, banner = False,
snappyPath = True, namespace = None, builtinOpen = True):
"""
Initialise a Regina python session.
Arguments:
quiet : If true, suppress informative messages that would
otherwise be written to standard output. If an error
occurs, details of the error will be written regardless.
readline : If true, attempt to enable tab completion.
snappyPath : Applies to platforms where SnapPy might not be installed
on the python path (e.g., macOS users with the SnapPy app
bundle). If true, this setup routine will (i) attempt
to locate SnapPy, and (ii) if successful, extend sys.path
to include the location of SnapPy's python module and its
dependencies.
banner : If true, print a welcome banner to standard output.
namespace : The global namespace in which the start-up library scripts
(if any) will be executed. This may be None, in which
case the caller's global namespace will be used.
builtinOpen: If true, sets "open" in the given namespace to Python's
builtin open() function. This is used to work around the
problem whereby "from regina import *", overrides Python's
open() function with Regina's. You will still be able to
access Regina's open() function by calling regina.open().
If the namespace argument above is None, then this option
has no effect.
"""
if readline:
# Enable tab completion through readline, if we can.
try:
import rlcompleter, readline
# readline by default completes an empty string, whereas if
# we press tab we want to insert an actual tab character,
# so we have our own completion function.
__internal_python_completer = readline.get_completer()
def regina_completer(text, state):
if not text:
return ('\t', None)[state]
else:
return __internal_python_completer(text, state)
readline.set_completer(regina_completer)
if 'libedit' in readline.__doc__:
# Some systems work with libedit, not libreadline, which
# supports a different set of commands.
readline.parse_and_bind('bind ^I rl_complete')
else:
readline.parse_and_bind('tab: complete')
except:
pass
if snappyPath:
# For the time being, only find SnapPy on macOS.
if sys.platform == 'darwin':
if sys.version_info[:2] == (3, 8):
# Ask macOS where SnapPy lives.
import subprocess
try:
app = subprocess.check_output(['mdfind',
'kMDItemDisplayName==SnapPy&&kMDItemKind==Application'])
if app:
app = app.strip().split('\n')[0]
except:
app = None
if not app:
app = '/Applications/SnapPy.app'
snappyLib = app + '/Contents/Resources/lib/python3.8'
snappyZip = app + '/Contents/Resources/lib/python38.zip'
if os.path.exists(snappyLib):
sys.path.append(snappyLib)
sys.path.append(snappyLib + '/site-packages.zip')
sys.path.append(snappyLib + '/lib-dynload')
if os.path.exists(snappyZip):
sys.path.append(snappyZip)
if banner:
print(engine.welcome())
if builtinOpen and namespace:
namespace['open'] = __builtins__['open']
def __execScript(namespace = None):
"""
For internal use by regina-python.
Executes a given python script.
The filename of the script should be in sys.argv[1], and
any arguments to the script should be in sys.argv[2:].
However, ipython sets things up a little differently (it includes two
additional arguments), and we attempt to compensate for this here.
SIDE-EFFECT: sys.argv will be truncated to include the script and
its arguments only (i.e., sys.argv[0] will be removed).
Arguments:
namespace : The global namespace in which the script will be executed.
This may be None, in which case the caller's global
namespace will be used.
"""
try:
__IPYTHON__
# Although python sets sys.argv = [ '-c', script, arg, ... ],
# ipython sets sys.argv as the full original command line:
# [ ipython, '-c', command, script, arg, ... ], or
# [ ipython, '-i', '-c', command, script, arg, ... ].
# Repair things here.
if len(sys.argv) >= 3 and sys.argv[1] == '-c':
sys.argv = [ '-c' ] + sys.argv[3:]
elif len(sys.argv) >= 4 and sys.argv[1:3] == ['-i', '-c']:
sys.argv = [ '-c' ] + sys.argv[4:]
except:
pass
script = __builtins__['open'](sys.argv[1]).read()
sys.argv = sys.argv[1:]
if namespace:
exec(script, namespace)
else:
exec(script)
|