为CLI脚本构建python包



我正在尝试构建我的第一个python包,并将其发布到PyPi。我的目标是通过CLI提供某种功能,这样最终用户就可以通过像pip3 install my-package这样的pip安装它,并直接从像my-package -fa first_argument -sa second_argument这样的终端使用它。

假设我的包裹名为fp-test-package。我制作了一个项目文件夹结构的模型,你可以在这里找到https://github.com/fabiopipitone/fp-test-package.

在模型中,我还插入了真实项目中所需的一个包(tqdm(作为需求,并保持完全相同的约定(外部文件夹和github repo上的连字符,内部包的下划线,助手和utils的相同位置和导入,等等(。这样,如果它在实体模型上工作,它就必须在真实的项目上工作。

以下是项目的目录结构

fp-test-package
├── fp_test_package
│   ├── __init__.py
│   ├── fp_test_package.py
│   ├── helpers
│   │   ├── arguments_checkers.py
│   │   ├── csv_handlers.py
│   │   └── utility_functions.py
│   └── utils
│       ├── __init__.py
│       ├── CustomLogger.py
│       └── TqdmLoggingHandler.py
├── LICENSE
├── README.rst
├── requirements.txt
├── setup.py
├── docs
└── tests

这是设置.py

from setuptools import setup, find_packages
with open("README.rst", "r") as fh:
long_description = fh.read()
setup( 
name ='fp-test-package', 
version ='0.0.1', 
description='Simple test building a CLI tool package',
long_description=long_description,
long_description_content_type='text/x-rst', 
license ='GPLv2', 
packages = find_packages(), 
entry_points ={ 
'console_scripts': [ 
'fp_test_package = fp_test_package.py:main'
] 
}, 
classifiers =( 
"Programming Language :: Python :: 3", 
"License :: OSI Approved :: GNU General Public License v2", 
"Operating System :: Linux", 
), 
keywords ='test packaging fabiopipitone', 
install_requires = ['tqdm>=4.49.0'], 
zip_safe = False
) 

我尝试了以下几条关于如何构建和发布CLI python包的说明,然后我从fp-test-package目录(与setup.py级别相同(中尝试了sudo python3 setup.py install。它似乎安装了包(我可以在pip3 freeze中找到条目fp-test-package==0.0.1(,但如果我在终端中尝试fp-test-package,它会返回fp-test-package: command not found,如果我尝试fp_test_package,它将返回:

Traceback (most recent call last):
File "/usr/local/bin/fp_test_package", line 11, in <module>
load_entry_point('fp-test-package==0.0.1', 'console_scripts', 'fp_test_package')()
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 490, in load_entry_point
return get_distribution(dist).load_entry_point(group, name)
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2854, in load_entry_point
return ep.load()
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2445, in load
return self.resolve()
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2451, in resolve
module = __import__(self.module_name, fromlist=['__name__'], level=0)
ModuleNotFoundError: No module named 'fp_test_package.py'

然后我使用了sudo pip3 uninstall fp_test_package,它成功地卸载了该包。我删除了之前创建的builddistfp_test_package.egg-info目录,并尝试使用pip3 install -e .(再次从fp-test-package内部(。它创建了fp_test_package.egg-info目录,但在控制台中运行fp_test_package时,它再次返回

Traceback (most recent call last):
File "/usr/local/bin/fp_test_package", line 11, in <module>
load_entry_point('fp-test-package==0.0.1', 'console_scripts', 'fp_test_package')()
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 490, in load_entry_point
return get_distribution(dist).load_entry_point(group, name)
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2854, in load_entry_point
return ep.load()
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2445, in load
return self.resolve()
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2451, in resolve
module = __import__(self.module_name, fromlist=['__name__'], level=0)
ModuleNotFoundError: No module named 'fp_test_package.py'

现在,我错过了什么?我想简单地打包并安装它,这样当用户键入fp-test-packagefp_test_package时,它会返回关于所需--export_path参数的错误(意味着脚本已正确启动(,就像我从控制台调用它时一样:

$ python3 fp_test_package/fp_test_package.py

usage: fp_test_package.py [-h] -ep EXPORT_PATH [-sa SECOND_ARGUMENT] [-ta THIRD_ARGUMENT]
fp_test_package.py: error: the following arguments are required: -ep/--export_path

我该怎么做?

编辑:我注意到@Dustin随后指出的关于setup.py的内容。事实上,我的一位同事发布了更改console_scripts部分的伪repo。现在,在我同事的机器上,一切似乎都如预期的那样工作。在我的例子中,在提取repo并重新运行python3 setup.py install之后,当从控制台调用fp_test_package时,它返回以下内容:

Traceback (most recent call last):
File "/usr/local/bin/fp_test_package", line 11, in <module>
load_entry_point('fp-test-package==0.0.1', 'console_scripts', 'fp_test_package')()
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 490, in load_entry_point
return get_distribution(dist).load_entry_point(group, name)
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2854, in load_entry_point
return ep.load()
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2445, in load
return self.resolve()
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2451, in resolve
module = __import__(self.module_name, fromlist=['__name__'], level=0)
File "/usr/local/bin/fp_test_package.py", line 4, in <module>
__import__('pkg_resources').run_script('fp-test-package==0.0.1', 'fp_test_package.py')
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 667, in run_script
self.require(requires)[0].run_script(script_name, ns)
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 1452, in run_script
raise ResolutionError(
pkg_resources.ResolutionError: Script 'scripts/fp_test_package.py' not found in metadata at '/usr/local/lib/python3.8/dist-packages/fp_test_package-0.0.1-py3.8.egg/EGG-INFO'

有什么想法吗?

您的setup.py具有:

entry_points ={ 
'console_scripts': [ 
'fp_test_package = fp_test_package.py:main'
] 
}, 

这相当于以下导入:

from fp_test_package.py import main as fp_test_package

问题是,您没有fp_test_package.py模块,而是有一个包含fp_test_package子模块(模块名称不包括扩展名(的fp_test_package模块(包含__init__.py文件的目录(。

假设您的fp_test_package.py文件定义了一个main函数,您可以将其更改为:

entry_points ={ 
'console_scripts': [ 
'fp_test_package = fp_test_package.fp_test_package:main'
] 
}, 

或者,您可以将main函数移动到fp_test_package/__init__.py,因此它可以是:

entry_points ={ 
'console_scripts': [ 
'fp_test_package = fp_test_package:main'
] 
}, 

好的,我最终在我的特定案例中发现了问题。在安装python3 setup.py install时,是什么导致了那个神秘的错误

pkg_resources.ResolutionError: Script 'scripts/fp_test_package.py' not found in metadata at '/usr/local/lib/python3.8/dist-packages/fp_test_package-0.0.1-py3.8.egg/EGG-INFO'

或使用pip3 install .安装时的等效错误

pkg_resources.ResolutionError: Script 'scripts/fp_test_package.py' not found in metadata at '/home/fabio/Desktop/test_python_package/fp-test-package/fp_test_package-0.0.1-py3.8.egg/EGG-INFO'

是$PATH变量中缺少正确启动脚本所需的路径。

总之,我所做的是:

  • 根据Dustin关于console_scripts的建议编辑setup.py文件
  • fp-test-package文件夹中构建和安装运行pip3 install .(如果需要可编辑版本,则为pip3 install -e .(的包
  • 检查脚本的二进制文件是用which fp_test_package(在我的例子中是/home/fabio/.local/bin/fp_test_package(创建的
  • path添加到$PATH环境变量中(在我的.bashrc.zshrc中,因为我使用zsh,所以我添加了一行export PATH="$HOME/.local/bin:$PATH"(

现在一切如预期。我不会删除repo的正确版本,所以如果有人想使用工作框架来启动项目,可以在那里找到。

最新更新