Why do imports fail in setuptools entry_point scripts, but not in python interpreter?

橙三吉。 提交于 2020-01-24 04:10:32

问题


I have the following project structure:

project
|-project.py
|-__init__.py
|-setup.py
|-lib
  |-__init__.py
  |-project
    |-__init__.py
    |-tools.py

with project.py:

from project.lib import *

def main():
    print("main")
    tool()

if __name__ == "__main__":
    main()

setup.py:

from setuptools import setup

setup(
    name = "project",
    version="1.0",
    packages = ["project", "project.lib"],
    package_dir = {"project": ".", "project.lib": 'lib/project'},
    entry_points={
        'console_scripts': [
            'project = project.project:main',
        ],
    },
)

tools.py:

def tool():
    print("tool")

If I run

import project.lib.tools
project.lib.tools.tool()

it works as expected, but running the command project fails with

Traceback (most recent call last):
  File "/usr/local/bin/project", line 9, in <module>
    load_entry_point('project==1.0', 'console_scripts', 'project')()
  File "/usr/local/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 568, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/usr/local/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 2720, in load_entry_point
    return ep.load()
  File "/usr/local/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 2380, in load
    return self.resolve()
  File "/usr/local/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 2386, in resolve
    module = __import__(self.module_name, fromlist=['__name__'], level=0)
  File "build/bdist.linux-x86_64/egg/project/project.py", line 3, in <module>
ImportError: No module named lib

I don't understand why the two interpreters don't have the same default import pathes.

The reason for this setup is that I want to be able to import project.lib.tools, but keep the directory structure with lib/project.

The complete distutils documentation seriously doesn't say a word on how one can import packages after they have been distributed (the difference of setuptools and distutils isn't less misterious - no way of knowing whether the behavior of distutils is extended here or not).

I'm using setuptools 18.4-1 with python 2.7 on Ubuntu 15.10.

If I change the project structure and setup.py as suggested in @AnttiHaapala's answer I'm getting

$ project
Traceback (most recent call last):
  File "/usr/local/bin/project", line 9, in <module>
    load_entry_point('project==1.0', 'console_scripts', 'project')()
  File "/usr/local/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 568, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/usr/local/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 2720, in load_entry_point
    return ep.load()
  File "/usr/local/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 2380, in load
    return self.resolve()
  File "/usr/local/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 2386, in resolve
    module = __import__(self.module_name, fromlist=['__name__'], level=0)
  File "build/bdist.linux-x86_64/egg/project/project.py", line 3, in <module>
ImportError: No module named lib

回答1:


Your project structure seems to be b0rken. The standard layout for a distribution is that the setup.py is on the top-level. Your project would then have 1 (top-level) package, namely project, with sub-package project.lib. Thus we get the following directory layout:

Project-0.42/
 +- project/
 |    +- __init__.py
 |    +- lib/
 |    |   +- __init__.py
 |    |   +- tools.py
 |    +- project.py
 +- setup.py

Then in your setup.py you can simply do

from setuptools import find_packages

setup(
    ...
    # remove package_dir, it is unnecessary
    packages=find_packages(),
    ...
)

The package_dir really does not handle top-level + sub-packages simultaneously very well. After that pip remove project so many times that you can be certain you do not have any buggy versions of it installed in the site-packages, and then run python setup.py develop to link the source into site-packages.


After that, the problem is that you're using Python 2 with its broken import system which assumes relative imports. In project.py, your import project.lib assumes a relative import by default, and it tries to actually import project.project.lib. As this is not what you want, you should add

from __future__ import absolute_import

at the top of that file. I seriously suggest that you add this (and why not also the division import if you're using / operator anywhere at all), to avoid these pitfalls and to stay Python 3 compatible.



来源:https://stackoverflow.com/questions/34803310/why-do-imports-fail-in-setuptools-entry-point-scripts-but-not-in-python-interpr

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!