首页 > 解决方案 > 构建 python 模块,使其可以在带有和不带有 -m 开关的命令行中运行

问题描述

背景

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

期望的结果

可以使用以下方式运行模块:

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

结构

按照这个答案,我想了解-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

退货

ImportError:没有名为“实用程序”的模块

然而

python my_module --help

工作没有问题

期望的结果

标签: pythonpython-3.xcommand-linemodulepython-import

解决方案


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.


推荐阅读