Repository: ipython/ipython

Issue:
%save magic fails in clients without stdin if file already exists
`%save` fails in the nb/qtconsole if a file already exists, because it tries to ask the user interactively and this produces a `StdinNotImplementedError`.  We should in that case capture the exception, print instead a message and offer a -f option to support forced writes.



Discussion:
In some other case that tried to prompt the user, we went with printing a warning and overwriting the file, but I forget what that was.

For something as serious as overwriting a whole .py file, I somehow
feel more comfortable requiring an explicit -f switch... What do you
think?

That makes sense, I just thought I'd note the contrast. Should we change `%hist -f` to behave the same way? We'd need to call the 'force' option something other than f if so, as that means file there. y for yes would be an obvious choice.

Also, we should get `%save` to use the `ask_yes_no` function - that's why we didn't spot it when we were doing the others.

for reference, the previous similar case Thomas mentions is `%reset` as it was described in #1268 and implemented in #1288

And at some point we've done the same for `%hist -f`, which is more similar to `%save`.


Resolving patch:
diff --git a/IPython/core/magics/code.py b/IPython/core/magics/code.py
--- a/IPython/core/magics/code.py
+++ b/IPython/core/magics/code.py
@@ -21,7 +21,7 @@
 from urllib2 import urlopen
 
 # Our own packages
-from IPython.core.error import TryNext
+from IPython.core.error import TryNext, StdinNotImplementedError
 from IPython.core.macro import Macro
 from IPython.core.magic import Magics, magics_class, line_magic
 from IPython.core.oinspect import find_file, find_source_lines
@@ -57,6 +57,9 @@ def save(self, parameter_s=''):
           so that magics are loaded in their transformed version to valid
           Python.  If this option is given, the raw input as typed as the
           command line is used instead.
+          
+          -f: force overwrite.  If file exists, %save will prompt for overwrite
+          unless -f is given.
 
         This function uses the same syntax as %history for input ranges,
         then saves the lines to the filename you specify.
@@ -67,14 +70,19 @@ def save(self, parameter_s=''):
         If `-r` option is used, the default extension is `.ipy`.
         """
 
-        opts,args = self.parse_options(parameter_s,'r',mode='list')
+        opts,args = self.parse_options(parameter_s,'fr',mode='list')
         raw = 'r' in opts
+        force = 'f' in opts
         ext = u'.ipy' if raw else u'.py'
         fname, codefrom = unquote_filename(args[0]), " ".join(args[1:])
         if not fname.endswith((u'.py',u'.ipy')):
             fname += ext
-        if os.path.isfile(fname):
-            overwrite = self.shell.ask_yes_no('File `%s` exists. Overwrite (y/[N])? ' % fname, default='n')
+        if os.path.isfile(fname) and not force:
+            try:
+                overwrite = self.shell.ask_yes_no('File `%s` exists. Overwrite (y/[N])? ' % fname, default='n')
+            except StdinNotImplementedError:
+                print "File `%s` exists. Use `%%save -f %s` to force overwrite" % (fname, parameter_s)
+                return
             if not overwrite :
                 print 'Operation cancelled.'
                 return


Test patch: