title: python access functions in ipython notebook
body: <p>I have a function stored in a .py file, call it my_methods.</p>
  
  <pre><code>def print_text(myText):
      print myText
  </code></pre>
  
  <p>I'm using ipython notebook to do my development (local server), and the my_methods file is frequently changing.  </p>
  
  <p>I'd like to use runipy to run other ipython notebooks via a shell script that reference the functions within my_methods.  For example, one ipython notebook being launched from a shell script would look like this:</p>
  
  <pre><code>import my_methods as mm
  
  mm.print_text("print me")
  </code></pre>
  
  <p>How do I set this up so that the import my_methods line can get the print_text function from the ipython notebook (.ipynb), instead of the .py version?  Currently, I'd have to download the my_methods notebook as a .py file, which is causing version control issues (the .ipynb version of my_methods is different than the downloaded .py version)</p>
  
  <p>Thanks for the help!</p>
  
  <p><strong>EDIT</strong></p>
  
  <p>So after reading through the blog post that was shown as the answer I made a slight modification to the find_notebook function.  If a path for the ipython notebook isn't supplied,I just had it read through sys.path, rather than the current directory.  I'm also not running on a linux machine like they were, so it made it easier for me to maintain all of my added python files in a separate directory, which is included in my path variable.  I saved the following code in a module called iPyLoader, and then import it first in my other modules:</p>
  
  <pre class="lang-python prettyprint-override"><code>import io, os, sys, types
  from IPython import get_ipython
  from IPython.nbformat import current
  from IPython.core.interactiveshell import InteractiveShell
  
  def find_notebook(fullname, path=None):
      """find a notebook, given its fully qualified name and an optional path
  
      This turns "foo.bar" into "foo/bar.ipynb"
      and tries turning "Foo_Bar" into "Foo Bar" if Foo_Bar
      does not exist.
      """
      name = fullname.rsplit('.', 1)[-1]
      if not path:
          path = sys.path #EDITED HERE RATHER THAN CURRENT DIRECTORY
      for d in path:
          nb_path = os.path.join(d, name + ".ipynb")
          if os.path.isfile(nb_path):
              return nb_path
          # let import Notebook_Name find "Notebook Name.ipynb"
          nb_path = nb_path.replace("_", " ")
          if os.path.isfile(nb_path):
              return nb_path
  
  class NotebookLoader(object):
      """Module Loader for IPython Notebooks"""
      def __init__(self, path=None):
          self.shell = InteractiveShell.instance()
          self.path = path
  
      def load_module(self, fullname):
          """import a notebook as a module"""
          path = find_notebook(fullname)
  
          print ("importing IPython notebook from %s" % path)
  
          # load the notebook object
          with io.open(path, 'r', encoding='utf-8') as f:
              nb = current.read(f, 'json')
  
  
          # create the module and add it to sys.modules
          # if name in sys.modules:
          #    return sys.modules[name]
          mod = types.ModuleType(fullname)
          mod.__file__ = path
          mod.__loader__ = self
          mod.__dict__['get_ipython'] = get_ipython
          sys.modules[fullname] = mod
  
          # extra work to ensure that magics that would affect the user_ns
          # actually affect the notebook module's ns
          save_user_ns = self.shell.user_ns
          self.shell.user_ns = mod.__dict__
  
          try:
            for cell in nb.worksheets[0].cells:
              if cell.cell_type == 'code' and cell.language == 'python':
                  # transform the input to executable Python
                  code = self.shell.input_transformer_manager.transform_cell(cell.input)
                  # run the code in themodule
                  exec(code, mod.__dict__)
          finally:
              self.shell.user_ns = save_user_ns
          return mod
  
  class NotebookFinder(object):
      """Module finder that locates IPython Notebooks"""
      def __init__(self):
          self.loaders = {}
  
      def find_module(self, fullname, path=None):
          nb_path = find_notebook(fullname, path)
          if not nb_path:
              return
  
          key = path
          if path:
              # lists aren't hashable
              key = os.path.sep.join(path)
  
          if key not in self.loaders:
              self.loaders[key] = NotebookLoader(path)
          return self.loaders[key]
  
  sys.meta_path.append(NotebookFinder())
  </code></pre>
  
  <p>then my sample file might run something like this</p>
  
  <pre><code>import iPyLoader
  import testnotebook as printer #this is an .ipynb file
  printer.myprinter("test")
  </code></pre>
  
  <p>and the output would be:</p>
  
  <pre><code>importing IPython notebook from C:\Python27\lib\testnotebook.ipynb
  test
  </code></pre>