BUG: Issue #755 qt IPythonWidget.execute_file fails if filename contains... #1122

Closed
wants to merge 12 commits into
from
View
@@ -130,6 +130,9 @@ def _extra_extension_changed(self, name, old, new):
if new:
# add to self.extensions
self.extensions.append(new)
+
+ # Extensions that are always loaded (not configurable)
+ default_extensions = List(Unicode, [u'storemagic'], config=False)
exec_files = List(Unicode, config=True,
help="""List of files to run at IPython startup."""
@@ -158,11 +161,9 @@ def init_extensions(self):
This uses the :meth:`ExtensionManager.load_extensions` to load all
the extensions listed in ``self.extensions``.
"""
- if not self.extensions:
- return
try:
self.log.debug("Loading IPython extensions...")
- extensions = self.extensions
+ extensions = self.default_extensions + self.extensions
for ext in extensions:
try:
self.log.info("Loading IPython extension: %s" % ext)
@@ -2,26 +2,24 @@
"""
%store magic for lightweight persistence.
-Stores variables, aliases and macros in IPython's database. Stored values will
-be automatically restored whenever the extension is loaded.
+Stores variables, aliases and macros in IPython's database.
-To enable this functionality, list it in your default profile
-`ipython_config.py` file::
+To automatically restore stored variables at startup, add this to your
+:file:`ipython_config.py` file::
- c.InteractiveShellApp.extensions = ['storemagic']
-
-Or to use it temporarily, run this in your IPython session::
-
- %load_ext storemagic
+ c.StoreMagic.autorestore = True
"""
from IPython.core.error import TryNext, UsageError
+from IPython.core.plugin import Plugin
+from IPython.testing.skipdoctest import skip_doctest
from IPython.utils import pickleshare
+from IPython.utils.traitlets import Bool, Instance
import inspect,pickle,os,sys,textwrap
from IPython.core.fakemodule import FakeModule
-
+
def restore_aliases(ip):
staliases = ip.db.get('stored_aliases', {})
for k,v in staliases.items():
@@ -53,6 +51,7 @@ def restore_data(ip):
restore_aliases(ip)
restore_dhist(ip)
+@skip_doctest
def magic_store(self, parameter_s=''):
"""Lightweight persistence for python variables.
@@ -183,6 +182,24 @@ def magic_store(self, parameter_s=''):
self.db[ 'autorestore/' + args[0] ] = obj
print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__)
+
+class StoreMagic(Plugin):
+ shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
+ autorestore = Bool(False, config=True)
+
+ def __init__(self, shell, config):
+ super(StoreMagic, self).__init__(shell=shell, config=config)
+ shell.define_magic('store', magic_store)
+
+ if self.autorestore:
+ restore_data(shell)
+
+_loaded = False
+
def load_ipython_extension(ip):
- ip.define_magic('store', magic_store)
- restore_data(ip)
+ """Load the extension in IPython."""
+ global _loaded
+ if not _loaded:
+ plugin = StoreMagic(shell=ip, config=ip.config)
+ ip.plugin_manager.register_plugin('storemagic', plugin)
+ _loaded = True
@@ -276,17 +276,22 @@ def execute_file(self, path, hidden=False):
path = os.path.normpath(path).replace('\\', '/')
# Perhaps we should not be using %run directly, but while we
- # are, it is necessary to quote filenames containing spaces or quotes.
- # Escaping quotes in filename in %run seems tricky and inconsistent,
- # so not trying it at present.
- if '"' in path:
- if "'" in path:
- raise ValueError("Can't run filename containing both single "
- "and double quotes: %s" % path)
- path = "'%s'" % path
- elif ' ' in path or "'" in path:
- path = '"%s"' % path
-
+ # are, it is necessary to quote or escape filenames containing spaces
+ # or quotes.
+
+ # In earlier code here, to minimize escaping, we sometimes quoted the
+ # filename with single quotes. But to do this, this code must be
+ # platform-aware, because run uses shlex rather than python string
+ # parsing, so that:
+ # * In Win: single quotes can be used in the filename without quoting,
+ # and we cannot use single quotes to quote the filename.
+ # * In *nix: we can escape double quotes in a double quoted filename,
+ # but can't escape single quotes in a single quoted filename.
+
+ # So to keep this code non-platform-specific and simple, we now only
+ # use double quotes to quote filenames, and escape when needed:
+ if ' ' in path or "'" in path or '"' in path:
+ path = '"%s"' % path.replace('"', '\\"')
self.execute('%%run %s' % path, hidden=hidden)
#---------------------------------------------------------------------------
@@ -177,9 +177,11 @@ def reinit_logging(self):
if open_log_file is not None:
self.log.removeHandler(self._log_handler)
self._log_handler = logging.StreamHandler(open_log_file)
- self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
- self._log_handler.setFormatter(self._log_formatter)
self.log.addHandler(self._log_handler)
+ # Add timestamps to log format:
+ self._log_formatter = logging.Formatter("%(asctime)s.%(msecs).03d [%(name)s] %(message)s",
+ datefmt="%Y-%m-%d %H:%M:%S")
+ self._log_handler.setFormatter(self._log_formatter)
# do not propagate log messages to root logger
# ipcluster app will sometimes print duplicate messages during shutdown
# if this is 1 (default):
@@ -86,6 +86,7 @@ def __init__(self, **kwargs):
self.pongstream.on_recv(self.handle_pong)
def start(self):
+ self.tic = time.time()
self.caller = ioloop.PeriodicCallback(self.beat, self.period, self.loop)
self.caller.start()
@@ -62,8 +62,9 @@ New features
Terminal frontend by default (:ghpull:`838`).
* **%store**: The ``%store`` magic from earlier versions has been updated and
- placed in an extension, :ref:`extensions_storemagic`. Add 'storemagic' to ``c.InteractiveShellApp.extensions``
- in ipython_config.py to enable it (:ghpull:`1029`).
+ re-enabled (:ref:`extensions_storemagic`; :ghpull:`1029`). To autorestore
+ stored variables on startup, specify ``c.StoreMagic.autorestore = True`` in
+ :file:`ipython_config.py`.