如何为基本软件包设置配置__main__.py,__ init__.py和__setup__.py?

Ale*_*ail 41 python pip setuptools

背景:

我有一个像这样的目录结构:

Package/
    setup.py
    src/
        __init__.py
        __main__.py 
        code.py
Run Code Online (Sandbox Code Playgroud)

我希望能够以很多不同的方式运行代码.

  1. pip install Package然后python然后from Package import *

  2. python -m Package 应该做的事情 __main__.py

  3. python __main__.py这也应该做的事情,__main__.py但这一次,我们假设你已经下载了源而不是pip installing.

现在我已经让前两个工作了,但设置很乱:

setup.py:

setup(
    name='Package',
    packages=['Package'],
    package_dir={'Package': 'src'},
    ...
    entry_points={ 'console_scripts': ['Package = src.__main__:main' ] }
Run Code Online (Sandbox Code Playgroud)

__init__.py:

from Package.code import .......
Run Code Online (Sandbox Code Playgroud)

__main__.py:

from . import .......
Run Code Online (Sandbox Code Playgroud)

对我来说更有意义的是两种情况都要写

from code import ........
Run Code Online (Sandbox Code Playgroud)

但这给了我导入错误.

题:

我的方式真的是唯一的方式吗?

最重要的是,我如何支持第三个用例?现在,python __main__.py抛出

File "__main__.py", line 10, in <module>
    from . import code
ImportError: cannot import name 'class defined in code.py'
Run Code Online (Sandbox Code Playgroud)

笔记:

我读过了

a_g*_*est 30

你几乎拥有所需的一切(甚至更多)!我会使用以下设置:

code.py:

foo = 1
Run Code Online (Sandbox Code Playgroud)

__init__.py:

from .code import foo
Run Code Online (Sandbox Code Playgroud)

在这里进行相对导入因为__init__.py将在导入整个包时使用.请注意,我们使用.-syntax 将导入显式标记为相对,因为这是Python 3所必需的(如果你这样做,则在Python 2中from __future__ import absolute_import).

__main__.py:

from Package import foo

print('foo = ', foo)
Run Code Online (Sandbox Code Playgroud)

这是包的主脚本,因此我们使用绝对import语句.通过这样做,我们假设已经安装了包(或者至少已经放在路径上); 这就是应该处理包裹的方式!你可能会认为这与你的第三个用例的冲突,但实际上没有理由pip install一个包的时候.这真的不是什么大问题(特别是在使用时virtualenv)!

如果您需要修改源文件并通过运行__main__.py文件轻松观察更改,那么您只需使用-e("可编辑")开关安装软件包:( pip install -e .假设您在目录中Package).但是,使用当前的目录结构,这将无法工作,因为-e交换机将放置一个egg-link包含该setup.py文件的目录; 这个目录不包含一个名为的包Package,src而是(我有一个问题).

相反,如果您按照约定在包本身之后命名包源的根目录(这是Package您的示例),那么安装-e并不是问题:Python 确实Package在相应的目录中找到所需的包:

$ tree Package/
Package/
??? setup.py
??? Package   <-- Renamed "src" to "Package" because that's the package's name.
    ??? code.py
    ??? __init__.py
    ??? __main__.py
Run Code Online (Sandbox Code Playgroud)

这也让你省略了package_dir={'Package': 'src'}in 的额外定义setup.py.

关于以下内容的注释setup.py:对于您指定的三个用例,无需定义入口点.那就是你可以跳过这条线entry_points={ 'console_scripts': ['Package = src.__main__:main' ] }.通过运输__main__.py模块python -m Package将很容易执行此模块中的代码.您还可以添加额外的if子句:

def main():
    print('foo = ', foo)

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

另一方面,入口点允许您直接__main__.main从CLI 执行代码; 正在运行的$ Package将执行相应的代码.

概括

最重要的是我pip install在处理包时总是使用.为什么不,特别是如果你已经创建了一个setup.py文件?如果要"实时"应用对程序包的更改,则可以使用该-e开关进行安装(这可能需要重命名该src文件夹,请参见上文).因此,您的第三个用例将显示为"下载源代码和pip install (-e) Package(在virtualenv中);然后您可以运行python __main__.py".


编辑

运行__main__.pypip install

如果你不想通过pip安装软件包但仍然能够运行__main__.py脚本,我仍然会使用上面的设置.然后,我们需要确保的from Package import ...声明(S)还在后面,这可以通过扩大进口路径来实现(注意,这需要src重命名为软件包的名称目录!).

修改 PYTHONPATH

对于Linux bash,您可以按如下方式设置Pythonpath:

export PYTHONPATH=$PYTHONPATH:/path/to/Package
Run Code Online (Sandbox Code Playgroud)

或者如果您在以下目录中__main__.py:

export PYTHONPATH=$PYTHONPATH:`cd ..; pwd`
Run Code Online (Sandbox Code Playgroud)

当然,不同的操作系统有不同的方式.

扩展路径 __main__.py

您(或者更确切地说是您的同事)可以将以下行添加到脚本的顶部(在from Package import ...语句之前):

import sys
sys.path.append('/path/to/Package')
Run Code Online (Sandbox Code Playgroud)

扩展路径 sitecustomize.py

您可以放置sitecustomize.pylib/python3.5/site-packages/Python安装目录中命名的模块,该模块包含以下行:

import sys
sys.path.append('/path/to/Package')
Run Code Online (Sandbox Code Playgroud)

创建一个单独的顶级main.py脚本

所以你有以下布局:

$ tree Package/
Package/
??? main.py   <-- Add this file.
??? setup.py
??? src
    ??? code.py
    ??? __init__.py
    ??? __main__.py
Run Code Online (Sandbox Code Playgroud)

哪里main.py包含

import src.__main__
Run Code Online (Sandbox Code Playgroud)

现在__main__.py被视为src包的一部分,相对导入将起作用.而不是跑步,python src/__main__.py你现在就跑python main.py.