Repository: xonsh/xonsh

Issue:
inspect code with stdlib inspect
In a xonsh session:

```
$ def foo(): pass
$ import inspect
$ inspect.getsource(foo)
```

with a clean xonsh, got `OSError`:

```
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/xonsh/__amalgam__.py", line 11676, in default
    run_compiled_code(code, self.ctx, None, 'single')
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/xonsh/__amalgam__.py", line 1965, in run_compiled_code
    func(code, glb, loc)
  File "<xonsh-code>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py", line 944, in getsource
    lines, lnum = getsourcelines(object)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py", line 931, in getsourcelines
    lines, lnum = findsource(object)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py", line 762, in findsource
    raise OSError('could not get source code')
OSError: could not get source code
```

With a xonshrc setup, I got:

```
$ inspect.getsource(foo)
'import os.path\n'
```
This is the first line of a local xsh file loaded when xonsh got launched, see [here](https://github.com/mitnk/dotfiles/blob/master/xonsh/xonshrc#L99).

super help??:

```
$ foo??
Type:         function
String form:  <function foo at 0x104099488>
File:         /Users/hugo/.local/share/xonsh_ext/xonshrc.xsh
Definition:   ()
Source:
import os.path
<function __main__.foo>
```


Discussion:
i can reproduce the bug with inspect getting the wrong source, i have a xontrib i'm testing and using `load myxontrib` in xonshrc. Then when i define a function in the same manner in interactive session as above and try to get the source with inspect what i'm actually getting is the source from this xontrib. This seems like a bug, no idea how/why.

Hm. I'm not sure this is related, but in stack traces, things get attributed to the `vox_autocomplete` xontrib that shouldn't be.


Resolving patch:
diff --git a/xonsh/execer.py b/xonsh/execer.py
--- a/xonsh/execer.py
+++ b/xonsh/execer.py
@@ -54,6 +54,7 @@ def __init__(
         parser_args = parser_args or {}
         self.parser = Parser(**parser_args)
         self.filename = filename
+        self._default_filename = filename
         self.debug_level = debug_level
         self.unload = unload
         self.scriptcache = scriptcache
@@ -127,6 +128,7 @@ def compile(
         """
         if filename is None:
             filename = self.filename
+            self.filename = self._default_filename
         if glbs is None or locs is None:
             frame = inspect.stack()[stacklevel][0]
             glbs = frame.f_globals if glbs is None else glbs
diff --git a/xonsh/imphooks.py b/xonsh/imphooks.py
--- a/xonsh/imphooks.py
+++ b/xonsh/imphooks.py
@@ -108,7 +108,7 @@ def get_filename(self, fullname):
 
     def get_data(self, path):
         """Gets the bytes for a path."""
-        raise NotImplementedError
+        raise OSError
 
     def get_code(self, fullname):
         """Gets the code object for a xonsh file."""
@@ -116,17 +116,23 @@ def get_code(self, fullname):
         if filename is None:
             msg = "xonsh file {0!r} could not be found".format(fullname)
             raise ImportError(msg)
-        with open(filename, "rb") as f:
-            src = f.read()
-        enc = find_source_encoding(src)
-        src = src.decode(encoding=enc)
-        src = src if src.endswith("\n") else src + "\n"
+        src = self.get_source(filename)
         execer = self.execer
         execer.filename = filename
         ctx = {}  # dummy for modules
         code = execer.compile(src, glbs=ctx, locs=ctx)
         return code
 
+    def get_source(self, fullpath):
+        if fullpath is None:
+            raise ImportError("could not find fullpath to module")
+        with open(fullpath, "rb") as f:
+            src = f.read()
+        enc = find_source_encoding(src)
+        src = src.decode(encoding=enc)
+        src = src if src.endswith("\n") else src + "\n"
+        return src
+
 
 #
 # Import events


Test patch: