July 2018 Update
Most people should now use pip install setuptools (possibly with sudo).
Some may need to (re)install the python-setuptools package via their package manager (apt-get install, yum install, etc.).
This issue can be highly dependent on your OS and dev environment. See the legacy/other answers below if the above isn't working for you.
Explanation
This error message is caused by a missing/broken Python setuptools package. Per Matt M.'s comment and setuptools issue #581, the bootstrap script referred to below is no longer the recommended installation method.
The bootstrap script instructions will remain below, in case it's still helpful to anyone.
Legacy Answer
I encountered the same ImportError today while trying to use pip. Somehow the setuptools package had been deleted in my Python environment.
To fix the issue, run the setup script for setuptools:
wget https://bootstrap.pypa.io/ez_setup.py -O - | python
(or if you don't have wget installed (e.g. OS X), try
curl https://bootstrap.pypa.io/ez_setup.py | python
possibly with sudo prepended.)
If you have any version of distribute, or any setuptools below 0.6, you will have to uninstall it first.*
See Installation Instructions for further details.
* If you already have a working distribute, upgrading it to the "compatibility wrapper" that switches you over to setuptools is easier. But if things are already broken, don't try that.
July 2018 Update
Most people should now use pip install setuptools (possibly with sudo).
Some may need to (re)install the python-setuptools package via their package manager (apt-get install, yum install, etc.).
This issue can be highly dependent on your OS and dev environment. See the legacy/other answers below if the above isn't working for you.
Explanation
This error message is caused by a missing/broken Python setuptools package. Per Matt M.'s comment and setuptools issue #581, the bootstrap script referred to below is no longer the recommended installation method.
The bootstrap script instructions will remain below, in case it's still helpful to anyone.
Legacy Answer
I encountered the same ImportError today while trying to use pip. Somehow the setuptools package had been deleted in my Python environment.
To fix the issue, run the setup script for setuptools:
wget https://bootstrap.pypa.io/ez_setup.py -O - | python
(or if you don't have wget installed (e.g. OS X), try
curl https://bootstrap.pypa.io/ez_setup.py | python
possibly with sudo prepended.)
If you have any version of distribute, or any setuptools below 0.6, you will have to uninstall it first.*
See Installation Instructions for further details.
* If you already have a working distribute, upgrading it to the "compatibility wrapper" that switches you over to setuptools is easier. But if things are already broken, don't try that.
sudo apt-get install --reinstall python-pkg-resources
fixed it for me in Debian. Seems like uninstalling some .deb packages (twisted set in my case) has broken the path python uses to find packages
python - How to dynamically add and load entry points? - Stack Overflow
installation error: `from pkg_resources import load_entry_point` fails
setuptools - Explain Python entry points? - Stack Overflow
use pkg_resources on each located package to find its entry points
I needed to do something similar to load a dummy plugin for test purposes. This differs slightly from your use-case in that I was specifically trying to avoid needing to define the entry points in the package (as it is just test code).
I found I could dynamically insert entries into the pkg_resources data structures as follows:
import pkg_resources
# Create the fake entry point definition
ep = pkg_resources.EntryPoint.parse('dummy = dummy_module:DummyPlugin')
# Create a fake distribution to insert into the global working_set
d = pkg_resources.Distribution()
# Add the mapping to the fake EntryPoint
d._ep_map = {'namespace': {'dummy': ep}}
# Add the fake distribution to the global working_set
pkg_resources.working_set.add(d, 'dummy')
This, at run time, added an entry point called 'dummy' to 'namespace', which would be the class 'DummyPlugin' in 'dummy_module.py'.
This was determined through use of the setuptools docs, and dir() on the objects to get more info as needed.
Docs are here: http://setuptools.readthedocs.io/en/latest/pkg_resources.html
You might especially look at http://setuptools.readthedocs.io/en/latest/pkg_resources.html#locating-plugins if all you need to do is load a plugin that you have just stored to your local filesystem.
It's more than at least 5 years, since the time when I first asked myself almost the same question, and your question now is an impulse to finally find it out.
For me it was as well interesting, if one can add entry points from the same directory as the script without installation of a package. Though I always knew that the only contents of the package might be some meta with entry points looking at some other packages.
Anyway, here is some setup of my directory:
ep_test newtover$ tree
.
├── foo-0.1.0.dist-info
│ ├── METADATA
│ └── entry_points.txt
└── foo.py
1 directory, 3 files
Here is the contents of foo.py:
ep_test newtover$ cat foo.py
def foo1():
print 'foo1'
def foo2():
print 'foo2'
Now let's open ipython:
In [1]: def write_ep(lines): # a helper to update entry points file
...: with open('foo-0.1.0.dist-info/entry_points.txt', 'w') as f1:
...: print >> f1, '\n'.join(lines)
...:
In [2]: write_ep([ # only one entry point under foo.test
...: "[foo.test]",
...: "foo_1 = foo:foo1",
...: ])
In [3]: !cat foo-0.1.0.dist-info/entry_points.txt
[foo.test]
foo1 = foo:foo1
In [4]: import pkg_resources
In [5]: ws = pkg_resources.WorkingSet() # here is the answer on the question
In [6]: list(ws.iter_entry_points('foo.test'))
Out[6]: [EntryPoint.parse('foo_1 = foo:foo1')]
In [7]: write_ep([ # two entry points
...: "[foo.test]",
...: "foo_1 = foo:foo1",
...: "foo_2 = foo:foo2"
...: ])
In [8]: ws = pkg_resources.WorkingSet() # a new instance of WorkingSet
With default parameters WorkingSet just revisits each entry in sys.path, but you can narrow the list. pkg_resources.iter_entry_points is bound to a global instance of WorkingSet.
In [9]: list(ws.iter_entry_points('foo.test')) # both are visible
Out[9]: [EntryPoint.parse('foo_1 = foo:foo1'), EntryPoint.parse('foo_2 = foo:foo2')]
In [10]: foos = [ep.load() for ep in ws.iter_entry_points('foo.test')]
In [11]: for func in foos: print 'name is {}'.format(func.__name__); func()
name is foo1
foo1
name is foo2
foo2
And the contents of METADATA as well:
ep_test newtover$ cat foo-0.1.0.dist-info/METADATA
Metadata-Version: 1.2
Name: foo
Version: 0.1.0
Summary: entry point test
UPD1: I thought it over once again and now understand that you need an additional step before using the new plugins: you need to reload the modules.
This might be as easy as:
In [33]: modules_to_reload = {ep1.module_name for ep1 in ws.iter_entry_points('foo.test')}
In [34]: for module_name in modules_to_reload:
....: reload(__import__(module_name))
....:
But if a new version of your plugins package is based on significant changes in other used modules, you might need a particular order of reloading and reloading of those changed modules. This might become a cumbersome task, so that restarting the bot would be the only way to go.
An "entry point" is typically a function (or other callable function-like object) that a developer or user of your Python package might want to use, though a non-callable object can be supplied as an entry point as well (as correctly pointed out in the comments!).
The most popular kind of entry point is the console_scripts entry point, which points to a function that you want made available as a command-line tool to whoever installs your package. This goes into your setup.py script like:
entry_points={
'console_scripts': [
'cursive = cursive.tools.cmd:cursive_command',
],
},
I have a package I've just deployed called cursive.tools, and I wanted it to make available a "cursive" command that someone could run from the command line, like:
$ cursive --help
usage: cursive ...
The way to do this is define a function, like maybe a cursive_command function in the file cursive/tools/cmd.py that looks like:
def cursive_command():
args = sys.argv[1:]
if len(args) < 1:
print "usage: ..."
and so forth; it should assume that it's been called from the command line, parse the arguments that the user has provided, and ... well, do whatever the command is designed to do.
Install the docutils package for a great example of entry-point use: it will install something like a half-dozen useful commands for converting Python documentation to other formats.
EntryPoints provide a persistent, filesystem-based object name registration and name-based direct object import mechanism (implemented by the setuptools package).
They associate names of Python objects with free-form identifiers. So any other code using the same Python installation and knowing the identifier can access an object with the associated name, no matter where the object is defined. The associated names can be any names existing in a Python module; for example name of a class, function or variable. The entry point mechanism does not care what the name refers to, as long as it is importable.
As an example, let's use (the name of) a function, and an imaginary python module with a fully-qualified name 'myns.mypkg.mymodule':
def the_function():
"function whose name is 'the_function', in 'mymodule' module"
print "hello from the_function"
Entry points are registered via an entry points declaration in setup.py. To register the_function under entrypoint called 'my_ep_func':
entry_points = {
'my_ep_group_id': [
'my_ep_func = myns.mypkg.mymodule:the_function'
]
},
As the example shows, entry points are grouped; there's corresponding API to look up all entry points belonging to a group (example below).
Upon a package installation (ie. running 'python setup.py install'), the above declaration is parsed by setuptools. It then writes the parsed information in special file. After that, the pkg_resources API (part of setuptools) can be used to look up the entry point and access the object(s) with the associated name(s):
import pkg_resources
named_objects = {}
for ep in pkg_resources.iter_entry_points(group='my_ep_group_id'):
named_objects.update({ep.name: ep.load()})
Here, setuptools read the entry point information that was written in special files. It found the entry point, imported the module (myns.mypkg.mymodule), and retrieved the_function defined there, upon call to pkg_resources.load().
Calling the_function would then be simple:
>>> named_objects'my_ep_func'
hello from the_function
Thus, while perhaps a bit difficult to grasp at first, the entry point mechanism is actually quite simple to use. It provides an useful tool for pluggable Python software development.
In case you have installed the setup.py file with the flag --editable
For example:
pip install --editable .
You have to install the package again without the --editable flag.
pip install .
It will not affect the changes you made locally in the package.
It looks like you haven't installed the package in any way, so you're relying on it happening to sit in the current working directory, or some other directory you've added to sys.path.
You didn't tell us how you "invoke awesome-tool from the command-line," but my guess is that you're doing it from a different working directory.
For example:
$ cd ~/virtualenvs/awesome-tool
$ python
>>> import awesometool.awesometool
>>> ^D
$ cd ..
$ python
>>> import awesometool.awesometool
ImportError: No module named awesome_tool.awesome_tool
$ cd awesome-tool
$ python ./bin/awesome-tool
<everything works>
$ cd bin
$ python awesome-tool
ImportError: No module named awesome_tool.awesome_tool