393

I want to import foo-bar.py, this works:

foobar = __import__("foo-bar")

This does not:

from "foo-bar" import *

My question: Is there any way that I can use the above format i.e., from "foo-bar" import * to import a module that has a - in it?

7
  • 43
    I'm guessing it was originally written as a script rather than as a module. Commented Dec 2, 2011 at 2:19
  • 5
    possible duplicate of Python Module with a dash, or hyphen (-) in its name Commented Feb 26, 2014 at 14:40
  • @MattiVirkkunen makepy.py of win32com will generate module with dash in it. too bad. comtypes solved this by converting it to underscore Commented Apr 11, 2014 at 1:45
  • 9
    @MattiVirkkunen I think Python should not limit the names I can give my directories. It is not its responsibility to do so. Commented Nov 11, 2016 at 12:34
  • 2
    Possible duplicate of Is it ok to use dashes in Python files when trying to import them? Commented May 17, 2017 at 4:31

8 Answers 8

279

Starting from Python 3.1, you can use importlib :

import importlib  
foobar = importlib.import_module("foo-bar")

( https://docs.python.org/3/library/importlib.html )

Sign up to request clarification or add additional context in comments.

4 Comments

I think this is the best solution, especially now that Python 2 is formally deprecated
how can I rename the module in this way?
@AliKhosro it will be named whatever variables you set it to. So if you use helloworld = importlib.import_module("foo-bar"), then you will reference fields in foo-bar using helloworld, e.g. helloworld.abc
I ended up just renaming the file to get code completion support in my dev tool.
153

In Python 2, you can't. foo-bar is not an identifier. rename the file to foo_bar.py


It's possible since Python 3.1+, see Julien's answer.


If import is not your goal (as in: you don't care what happens with sys.modules, you don't need it to import itself), just getting all of the file's globals into your own scope, you can use execfile

# contents of foo-bar.py
baz = 'quux'
>>> execfile('foo-bar.py')
>>> baz
'quux'
>>> 

3 Comments

Python 3.x What’s New In Python 3.0 Removed execfile(). Instead of execfile(fn) use exec(open(fn).read()) Also there is package importlib.
"if you don't care what happens with sys.modules", can you clarify what you meant? What is the condition for using execfile?
__name__ == "__main__" is True when execfile()
149

Solution: If you can't rename the module to match Python naming conventions, create a new module to act as an intermediary:

New module foo_proxy.py:

 tmp = __import__('foo-bar')
 globals().update(vars(tmp))

Module doing the import main.py:

 from foo_proxy import * 

4 Comments

I would never implement this. But I can't not give +1 for the sheer brilliance of this hack
you could actually do this without the foo_proxy.py file, assign the output of __import__(...) to sys.modules['foo_proxy']. Actually, don't do that, it's a terrible idea.
Cool just what I was looking for. There is a usecase, if one uses native libraries which are shipped with a distribution.
Note that the globals().update() call breaks if __name__ == '__main__' check, because __name__ afterwards contains name of the module you're loading.
59

If you can't rename the original file, you could also use a symlink:

ln -s foo-bar.py foo_bar.py

Then you can just do:

from foo_bar import *

2 Comments

Doesn't work with Windows unfortunately.
The *nix command above likely will not, but Windows does have its own symlink support (since Win7): mklink Link Target ref: howtogeek.com/howto/16226/…
14

Like other said you can't use a - in python naming, there are many workarounds, one such workaround which would be useful if you had to add multiple modules from a path is using sys.path

For example if your structure is like this:

foo-bar
├── barfoo.py
└── __init__.py

import sys
sys.path.append('foo-bar')

import barfoo

Comments

2

This was my scenario: I have a python library cloned in a git submodule which has a dash in its name:

|- python-my-lib
| `- mylib.py
`- my-script.py

It took me a long time to figure out the equivalent of:

# Do NOT use this!
sys.path.insert(1, './my-lib')
from mylib import MyClass

Appending the path is not an option, as it would only work if you run the script within the same directory. If you do /home/user/bin/my-script.py, this will fail.

This is the solution:

import importlib
mylib_module = importlib.import_module("python-my-lib.mylib")
MyClass = mylib_module.MyClass

Feel free to further improve this solution, if you know a simpler solution.

Comments

2

Python has issues with dash -. So use importlib instead. You can run your test scripts just like this -

# importlib, because python has issues with dash '-' in module names
import importlib
img2txt = importlib.import_module("img2txt-textextractor")

event_with_txt = {...}

event_with_no_txt = {...}


def test_no_text():
    response = img2txt.handler(event=event_with_txt, context='')
    assert response["body"] == '"Detect Me If You Can. "'

def test_detected_text():
    response = img2txt.handler(event=event_with_no_txt, context='')
    assert response["body"] == '"unable to find anything"'

Name your test code as test_someName.py. To run, from the same directory on terminal type -

pytest

Comments

1

in Python 3.6 I had the same problem "invalid syntax" when directly

import 'jaro-winkler' as jw

said "No module named 'jaro-winkler'" when using:

jw = __import__('jaro-winkler')

and importlib.import_module() same.

finally i use pip uninstall the jaro-winkler module...just FYI

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.