• Posts
  • RSS
  • ◂◂RSS
  • Contact

  • Importing Python By Path

    February 17th, 2011
    python, tech  [html]

    Update 2012-01-06: Warning: this post is what you get if you don't know about the function __import__ and try to reinvent it.

    Imagine I have two different python files that are drop-in replacements for each other, perhaps for some kind of plugin system:

      $ cat a/c.py
      def d():
         print "hello world a"
      $ cat b/c.py
      def d():
         print "hello world b"
      
    And futher imagine I want to use both of them in the same program. I might write:
      import c
      c.d()
      import c
      c.d()
      
    But that has no chance of working. Python doesn't know where to find 'c'. So I need to tell python where to look for my code by changing sys.path:
      sys.path.append("a")
      import c
      c.d()
      sys.path.append("b")
      import c
      c.d()
      
    This will work, ish. It will print "hello world a" twice. Part of this is that the path "a" is sys.path before "b". I really only want this sys.path change to last long enough for my import to work. So maybe I should do:
      class sys_path_containing(object):
         def __init__(self, fname):
             self.fname = fname
    
         def __enter__(self):
             self.old_path = sys.path
             sys.path = sys.path[:]
             sys.path.append(self.fname)
    
         def __exit__(self, type, value, traceback):
             sys.path = self.old_path
    
      with sys_path_containing("a"):
         import c
    
      c.d()
    
      with sys_path_containing("b"):
          import c
    
      c.d()
      
    This is closer to working. I define a context manager so that code run with sys_path_containing will see a different sys.path. So my first "import c" will see a sys.path like ["foo", "bar", "a"] and my second import will see ["foo", "bar", "b"]. Each is isolated from the other and from other system changes. Unfortunately, it still won't work, because python remembers what it has imported before and doesn't do it again, this will still only print "hello world a" twice. Switching the second "import c" to a "reload(c)" does fix this problem, but at the expense of you already having to know whether something is loaded. Switching to "del sys.modules['c']" and using __import__ would work, though. Let's make that change and put it all into a context manager that does most of the work for us:
      class imported(object):
         def __init__(self, fname):
             self.fname = os.path.abspath(fname)
    
         def __enter__(self):
             if not os.path.exists(self.fname):
                 raise ImportError("Missing file %s" % self.fname)
    
             self.old_path = sys.path
             sys.path = sys.path[:]
    
             file_dir, file_name = os.path.split(self.fname)
             sys.path.append(file_dir)
    
             file_base, file_ext = os.path.splitext(file_name)
             module = __import__(file_base)
             del sys.modules[file_base]
    
             return module
    
         def __exit__(self, type, value, traceback):
             sys.path = self.old_path
    
      with imported("a/c.py") as c:
         c.d()
    
      with imported("b/c.py") as c:
         c.d()
      
    This will print "hello world a" and then "hello world b". Yay!

    Recent posts on blogs I like:

    More on the Deutschlandtakt

    The Deutschlandtakt plans are out now. They cover investment through 2040, but even beforehand, there’s a plan for something like a national integrated timetable by 2030, with trains connecting the major cities every 30 minutes rather than hourly. But the…

    via Pedestrian Observations July 1, 2020

    How do cars fare in crash tests they're not specifically optimized for?

    Any time you have a benchmark that gets taken seriously, some people will start gaming the benchmark. Some famous examples in computing are the CPU benchmark specfp and video game benchmarks. With specfp, Sun managed to increase its score on 179.art (a su…

    via Posts on Dan Luu June 30, 2020

    Quick note on the name of this blog

    When I was 21 a friend introduced me to a volume of poems by the 14th-century Persian poet Hafiz, translated by Daniel Ladinsky. I loved them, and eventually named this blog for one of my favorite ones. At some point I read more and found that Ladinsky’s …

    via The whole sky June 21, 2020

    more     (via openring)


  • Posts
  • RSS
  • ◂◂RSS
  • Contact