Repository: ipython/ipython

Issue:
__file__ is not defined when file end with .ipy
I named my IPython file.ipy
I propose .ipy ? objections ?
I feel ipython should not be confused with plain python!

```
echo "#!/usr/bin/env ipython" > test.ipy
echo "print __file__" >> test.ipy

cat test.ipy
#!/usr/bin/env ipython
print __file__
```

```
chmod +x test.ipy
./test.ipy
> NameError: name '__file__' is not defined

ipython test.ipy
> NameError: name '__file__' is not defined

## run with plain python
python test.ipy
> ./test.ipy
SUCCESS:

## rename file to be .py seems to solve the issue
mv ./test.ipy ./test.py
ipython test.ipy
> ./test.py
SUCCESS:

./test.py
> ./test.py
SUCCESS:
```

Should we not trust files with different endings...  I have a feeling files not ending with .py are blacklisted to prevent issues with cgi or something... not to sure..  Perhaps it has something to do with the she-bang notation..?

debian kubuntu 12.10

```
ipython -v
0.12.1
```

Thanks



Discussion:
Yes, see `IPython.core.shellapp.InteractiveShellApp._exec_file`:

```
if full_filename.endswith('.ipy'):
    self.log.info("Running file in user namespace: %s" %
                  full_filename)
    self.shell.safe_execfile_ipy(full_filename)
else:
    # default to python, even without extension
    self.log.info("Running file in user namespace: %s" %
                  full_filename)
    # Ensure that __file__ is always defined to match Python behavior
    self.shell.user_ns['__file__'] = fname
    try:
        self.shell.safe_execfile(full_filename, self.shell.user_ns)
    finally:
        del self.shell.user_ns['__file__']
```

Running a file ending with `*.ipy` current is _exactly_ the same as opening up an ipython terminal and pasting the entire file into one cell.  (And in that way, `__file__` is not set).

Should this be changed?

I think it's a reasonable idea, but not very important - .ipy scripts are a convenience, and we don't want people relying on them for anything complex.

Without this patch.
How should I get the **file** string.. without renaming the file to .py?

I don't see the harm with having **file** set.

People often need to know the dynamic location of the file and its name in shell scripts.

part of old .sh scripts that I would like to move to IPython.

```
#!/bin/sh
frl="$(readlink -f $0)"
fdn="$(dirname $frl)"
fn="$(basename $frl)"
fnb="${fn%.*}"
fnbb="${fn%%.*}"
fnxx="${fn##*.}"

# ... many shell calls
```

Anyways ill just rename to .py

Well, you can rename to _anything_ except .ipy.

If you can just call it foo.py, absolutely do so. We don't want to encourage people to write code for IPython when standard Python code would do the job. .ipy scripts exist so you can use IPython syntax, like %magic functions and !shell commands.

But I agree with you that there should be a way to find the running filename from a .ipy script. We're quite busy at the moment, but if you want to dig into the code and make a pull request, that's great. Otherwise, we'll leave this issue open, and someone will get round to it at some point.


Resolving patch:
diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py
--- a/IPython/core/interactiveshell.py
+++ b/IPython/core/interactiveshell.py
@@ -2419,6 +2419,9 @@ def safe_execfile(self, fname, *where, **kw):
         dname = os.path.dirname(fname)
 
         with prepended_to_syspath(dname):
+            # Ensure that __file__ is always defined to match Python behavior
+            save_fname = self.user_ns.get('__file__',None)
+            self.user_ns['__file__'] = fname
             try:
                 py3compat.execfile(fname,*where)
             except SystemExit, status:
@@ -2439,6 +2442,8 @@ def safe_execfile(self, fname, *where, **kw):
                 if kw['raise_exceptions']:
                     raise
                 self.showtraceback()
+            finally:
+                self.user_ns['__file__'] = save_fname
 
     def safe_execfile_ipy(self, fname):
         """Like safe_execfile, but for .ipy files with IPython syntax.
@@ -2465,6 +2470,9 @@ def safe_execfile_ipy(self, fname):
         dname = os.path.dirname(fname)
 
         with prepended_to_syspath(dname):
+            # Ensure that __file__ is always defined to match Python behavior
+            save_fname = self.user_ns.get('__file__',None)
+            self.user_ns['__file__'] = fname
             try:
                 with open(fname) as thefile:
                     # self.run_cell currently captures all exceptions
@@ -2475,6 +2483,8 @@ def safe_execfile_ipy(self, fname):
             except:
                 self.showtraceback()
                 warn('Unknown failure executing file: <%s>' % fname)
+            finally:
+                self.user_ns['__file__'] = save_fname
 
     def safe_run_module(self, mod_name, where):
         """A safe version of runpy.run_module().
diff --git a/IPython/core/shellapp.py b/IPython/core/shellapp.py
--- a/IPython/core/shellapp.py
+++ b/IPython/core/shellapp.py
@@ -277,20 +277,12 @@ def _exec_file(self, fname):
             sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ]
         try:
             if os.path.isfile(full_filename):
+                self.log.info("Running file in user namespace: %s" % full_filename)
                 if full_filename.endswith('.ipy'):
-                    self.log.info("Running file in user namespace: %s" %
-                                  full_filename)
                     self.shell.safe_execfile_ipy(full_filename)
                 else:
                     # default to python, even without extension
-                    self.log.info("Running file in user namespace: %s" %
-                                  full_filename)
-                    # Ensure that __file__ is always defined to match Python behavior
-                    self.shell.user_ns['__file__'] = fname
-                    try:
-                        self.shell.safe_execfile(full_filename, self.shell.user_ns)
-                    finally:
-                        del self.shell.user_ns['__file__']
+                    self.shell.safe_execfile(full_filename, self.shell.user_ns)
         finally:
             sys.argv = save_argv
 


Test patch: