构建 python 模块,因此它可以从命令行 运行 使用和不使用 -m 开关

Structuring python module so it can be run from a command line with and without -m switch

背景

我正在研究 Python 包含许多脚本的模块。最终目标是在交互工作时通过 import 语句使该模块中的函数可导入,并通过命令行参数使模块的某些部分可执行。

期望的结果

模块可以 运行 使用:

python -m ./my_module --help
# No -m switch
python ./my_module --help

结构

this answer 之后,我想了解 -m 切换到 __main__.py__init__.py 文件的关系。当前结构如下

__main__.py

# Shebang line


###########
# Modules #
###########
import argparse
import logging
import tempfile


###################
# Package modules #
###################
from utilities import check_directory_access
# ...


#################
# Run functions #
#################
def run(args):
    """Run all functions with provided arguments"""
    # Start logging configuration
    # If file is not provided use temporary file
    if args.log_file is None:
        args.log_file = tempfile.NamedTemporaryFile(delete=False,
                                                    prefix='my_module',
                                                    suffix='.log').name
    # Create handlers: console
    # logging configuration
    logging.shutdown()


def main():
    """Process arguments and run main function"""
    parser = argparse.ArgumentParser(description='Dop stuff module',
                                     epilog='Epilog')
    parser.add_argument("-t", "--tables", nargs='+', dest='tables',
                        help="List tables to refresh", type=str)
    parser.add_argument("-l", "--log-file", dest='log_file',
                        type=str, help="Log file")
    parser.set_defaults(func=run)
    args = parser.parse_args()
    args.func(args)

if __name__ == "__main__":
    main()

__init__.py

###################
# Package modules #
###################
from .utilities import check_directory_access
# Other components

问题

运行:

python -m my_module --help

Returns

ImportError: No module named 'utilities'

python my_module --help

工作没有问题

期望的结果

Python 3 没有隐含的相对进口。使用绝对或显式相对导入:

from .utilities import check_directory_access
from my_module.utilities import check_directory_access

这使您的包可以使用 -m 开关。它还允许在交互式会话中 import my_module


作为文件夹存放的裸包不能直接执行。这是由于操作系统本身。如果您希望避免显式调用 python,则必须创建一个运行您的包的可执行文件。

将包存储为可执行的 zip 文件,或者创建一个运行包的脚本。

#!/usr/bin/env python3
import my_module.__main__

请注意,后者需要安装您的模块或直接与您的脚本相邻。如果您的模块可以安装,console_scripts entry_point 允许自动创建这样的脚本。

您不应将脚本驻留在您的包中 - 这需要您将 sys.path 更改为父目录,这可能会导致重复的模块。例如,utilities.py 可作为单独的模块 my_module.utilities utilities.