13

Say there is a folder, '/home/user/temp/a40bd22344'. The name is completely random and changes in every iteration. I need to be able to import this folder in Python using a fixed name, say 'project'. I know I can add this folder to sys.path to enable import lookup, but is there a way to replace 'a40bd22344' with 'project'?

Maybe some clever hacks in init.py?

Added:

It needs to be global - that is, other scripts loading 'project' via the standard:

import project

Have to work properly, loading a40bd22344 instead.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Art
  • 23,747
  • 29
  • 89
  • 101
  • 1
    Why can't you fix the process that created the temp file? It would be easier to fix this at the source instead of creating an elaborate work-around. What's wrong with fixing the name in /temp/ to be a real module name? – S.Lott Jul 08 '09 at 10:01
  • 1
    It's beyond of my control, folders are created by CI server. – Art Nov 27 '09 at 02:18

3 Answers3

25

Here's one way to do it, without touching sys.path, using the imp module in Python:

import imp

f, filename, desc = imp.find_module('a40bd22344', ['/home/user/temp/'])
project = imp.load_module('a40bd22344', f, filename, desc)

project.some_func()

Here is a link to some good documentation on the imp module:

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ars
  • 120,335
  • 23
  • 147
  • 134
  • 1
    This doesn't touch sys.path but I think using imp causes the package to be reloaded every time this code is executed. I'd prefer Alex's solution because it does the right thing even if executed multiple times. – Jon-Eric Nov 17 '09 at 15:29
19

You first import it with import:

>>> __import__('temp/a40bd22344')
<module 'temp/a40bd22344' from 'temp/a40bd22344/__init__.py'>

Then you make sure that this module gets known to Python as project:

>>> import sys
>>> sys.modules['project'] = sys.modules.pop('temp/a40bd22344')

After this, anything importing project in the current Python session will get the original module

>>> import project
>>> project
<module 'temp/a40bd22344' from 'temp/a40bd22344/__init__.py'>

This will work also for sub-modules: if you have a foobar.py in the same location you'll get

>>> import project.foobar
>>> project.foobar
<module 'project.foobar' from 'temp/a40bd22344/foobar.py'>

Addendum. Here's what I'm running:

>>> print sys.version
2.5.2 (r252:60911, Jul 31 2008, 17:28:52) 
[GCC 4.2.3 (Ubuntu 4.2.3-2ubuntu7)]
krawyoti
  • 19,695
  • 1
  • 23
  • 17
  • 1
    Excellent, except for the little detail that it does not work: in Python 2.6 and 3.1 it says "ImportError: Import by filename is not supported.", in 2.5 just "ImportError: No module named temp/a40bd22344", on the __import__ (just tried all three on my Mac to confirm I hadn't forgotten how __import__ works!-). I assume it works on your platform, krawyoti, and yours, Art, or you wouldn't have marked it as accepted, so I'm curious: what platforms are those? – Alex Martelli Jul 08 '09 at 14:03
  • Well it stands to reason that this loophole in __import__ I was using was fixed. Lazy of me not to try it in Python 2.6 too. – krawyoti Jul 08 '09 at 15:35
  • Alex, worked for me. Ubuntu/python 2.5/2.6. You need to use full path in __import__, or alternatively, add it to sys.path and then call __import__ just on folder name – Art Jul 09 '09 at 03:49
  • 3
    You may want to set the module's `__name__` attribute as well, so that code inspecting it will see it as the same as code which looks at `sys.modules`. – Glyph Jul 09 '09 at 11:22
17

Sure, project = __import__('a40bd22344') after sys.path is set properly will just work.

Suppose you want to do it in a function taking the full path as an argument and setting the global import of project properly (as well as magically making import project work afterwards in other modules). Piece of cake:

def weirdimport(fullpath):
  global project

  import os
  import sys
  sys.path.append(os.path.dirname(fullpath))
  try:
      project = __import__(os.path.basename(fullpath))
      sys.modules['project'] = project
  finally:
      del sys.path[-1]

this also leaves sys.path as it found it.

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • I've just added a clarification to my question, will it work in this case as well? – Art Jul 08 '09 at 08:43
  • 1
    Alex, put the __import__ statement in a try..finally bracket, so as to ensure that sys.path is always correctly restored. – krawyoti Jul 08 '09 at 10:05
  • 1
    @Art, as @krawyoti says that's accomplished by sticking it in sys.modules[project] as @krawyoti says. @krawyoti, good advice -- let me edit to reflect these two changes. – Alex Martelli Jul 08 '09 at 13:53