【问题标题】:setuptools dependency and import issuesetuptools 依赖和导入问题
【发布时间】:2012-04-08 08:37:40
【问题描述】:

我对 python 中的 setuptools 是半新手。我最近在我的项目中添加了一个依赖项,但遇到了依赖项问题。问题来了:

try:
    from setuptools import setup
except ImportError:
    from distutils.core import setup

from mypackage import VERSION

setup(
    name='mypackage',
    ...
    version=VERSION,
    packages=['mypackage'],
    install_requires=['six'])

问题是 mypackage 导入 six 并因此 setup.py 因from mypackage import VERSION 行而在全新安装时失败(尚未安装六个)。我已经通过破解一个虚拟模块导入(见下文)解决了这个问题,但我真的希望有一种更好的方法,不需要我在两个位置或一个单独的文件中维护版本号。

try:
    import six
except ImportError:
    # HACK so we can import the VERSION without needing six first   
    import sys
    class HackObj(object):
        def __call__(*args):
            return HackObj()
        def __getattr__(*args):
            return HackObj()
    sys.modules['six'] = HackObj()
    sys.modules['six.moves'] = HackObj()

【问题讨论】:

    标签: python setuptools


    【解决方案1】:

    作为一般规则,不要从setup.py 导入您的包,因为(正如您所意识到的)如果您还没有安装所有依赖项,它将无法安装您的包。

    相反,按照in this answer 的建议,创建一个单独的模块version.py(或类似的)没有任何外部依赖项定义VERSION。在您的setup.py 中执行execfile('mypackage/version.py'),瞧,您可以访问VERSION,而无需任何讨厌或不安全的黑客攻击。

    正如 medmunds 指出的那样,execfile 在 Python 3 中不可用,因此请改用:

    with open('mypackage/version.py') as f:
        exec(f.read())
    

    【讨论】:

    • 我忘了我发布了这个问题。另一个线程有一些很好的建议,其中一个类似于我现在的做法。很快添加我的解决方案。
    • execfile 解决方案非常满意,但它不适用于 Python 3。
    • 好的,对于 Python 2 3,使用 with open('mypackage/version.py') as f: exec(f.read()) 代替 execfile('mypackage/version.py')。 (来自stackoverflow.com/a/437857/647002
    【解决方案2】:

    我在提出这个问题后发布了几个月以来我一直在使用的解决方案。感谢 kynan 间接促使我回答这个问题。虽然 kynan 发布的解决方案很棒,但我个人不喜欢只为版本号添加单个文件,因为这意味着版本号将保存在 package.version.version 下的程序中。 PEP 396 表示版本号应位于名称 __version__ 下的包或模块命名空间的顶层。

    我现在使用的解决方案使用一个简单的正则表达式来解析 Python 程序中定义 __version__ 的行中的版本号。它看起来像:

    import os
    import re
    
    PACKAGE_NAME = 'thepackage'
    HERE = os.path.abspath(os.path.dirname(__file__))
    INIT = open(os.path.join(HERE, PACKAGE_NAME, '__init__.py')).read()
    README = open(os.path.join(HERE, 'README.md')).read()
    
    VERSION = re.search("__version__ = '([^']+)'", INIT).group(1)
    

    模块的过程类似,替换

    INIT = open(os.path.join(HERE, PACKAGE_NAME, '__init__.py')).read()
    

    INIT = open(os.path.join(HERE, '{0}.py'.format(PACKAGE_NAME))).read()
    

    【讨论】:

      【解决方案3】:

      虽然其他人已经适当地指出从 setup.py 导入您自己的包不是一个很好的做法,但在某些情况下,这种事情会以不同的方式出现 - 实际的设置过程需要一些包'直到所有依赖项安装完成后才可用。

      在这些情况下,setuptools 提供了一个解决方案:

      from setuptools import setup
      
      setup(
      name='mypackage',
      ...
      version=VERSION,
      packages=['mypackage'],
      setup_requires=['six'],
      install_requires=['six'])
      

      这实际上会下载六个的本地副本,以便在安装过程运行时可用。它还会将六个正确安装到您当前的环境中,以便在设置过程完成后可以使用。

      【讨论】:

        【解决方案4】:

        你不能这样吗?

        from pkg_resources import require
        version = require("module_name")[0].version
        

        否则,您可以尝试在 init.py 中编写版本,但老实说,我不知道这是否是一个好习惯。

        【讨论】:

        • 这个方法也有同样的问题:pkg_resources.DistributionNotFound: six.
        • 事实上你不应该需要六个,而是“mypackage”!
        • 是的,这就是我在那里所做的。 mypackage.__init__.py 包含import six 行,因此该错误和原始问题。
        猜你喜欢
        • 1970-01-01
        • 2014-05-27
        • 2015-11-21
        • 1970-01-01
        • 2012-04-25
        • 2014-12-27
        • 2015-04-22
        • 2016-05-18
        • 1970-01-01
        相关资源
        最近更新 更多