How to import module when module name has a '-' dash or hyphen in it?
Asked Answered
S

8

360

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?

Search answered 2/12, 2011 at 1:56 Comment(8)
Why do you have a module with a dash in its name?Gunner
I'm guessing it was originally written as a script rather than as a module.Badge
possible duplicate of Python Module with a dash, or hyphen (-) in its nameWingfooted
@MattiVirkkunen makepy.py of win32com will generate module with dash in it. too bad. comtypes solved this by converting it to underscoreGebhardt
@MattiVirkkunen I think Python should not limit the names I can give my directories. It is not its responsibility to do so.Oddball
@Zelphir: Do you also think Python should not limit the names you can give your variables and other identifiers? Surely you can see why it's silly to name a module (whose name is defined by the directory name) something that's not a valid identifier.Gunner
@MattiVirkkunen Actually I think variables are something inside Python and thus its "task" to care for, so that's OK. But maybe you have a point there about modules being its part as well. Anyway, it's not a big issue, just something one needs to know : )Oddball
Possible duplicate of Is it ok to use dashes in Python files when trying to import them?Britisher
T
149

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'
>>> 
Twain answered 2/12, 2011 at 2:0 Comment(3)
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.Plymouth
"if you don't care what happens with sys.modules", can you clarify what you meant? What is the condition for using execfile?Vermont
__name__ == "__main__" is True when execfile()Kitchens
A
250

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 )

Atropine answered 28/11, 2018 at 10:13 Comment(3)
I think this is the best solution, especially now that Python 2 is formally deprecatedLesley
how can I rename the module in this way?Kimmie
@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.abcFluecure
T
149

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'
>>> 
Twain answered 2/12, 2011 at 2:0 Comment(3)
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.Plymouth
"if you don't care what happens with sys.modules", can you clarify what you meant? What is the condition for using execfile?Vermont
__name__ == "__main__" is True when execfile()Kitchens
L
141

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 * 
Lindeman answered 2/12, 2011 at 2:9 Comment(3)
I would never implement this. But I can't not give +1 for the sheer brilliance of this hackUnmanned
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.Twain
Cool just what I was looking for. There is a usecase, if one uses native libraries which are shipped with a distribution.Hame
N
58

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 *
Nevertheless answered 9/1, 2014 at 16:14 Comment(2)
Doesn't work with Windows unfortunately.Ferdelance
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/…Cleavland
C
12

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
Chill answered 11/4, 2019 at 8:1 Comment(0)
S
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.

Suberin answered 22/5, 2022 at 9:41 Comment(0)
J
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
Janettejaneva answered 7/10, 2022 at 12:1 Comment(0)
V
0

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

Vesper answered 21/4, 2021 at 8:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.