我正在尝试为 Python 项目创建一个 Homebrew 公式。
这是自制公式:
class Scanman < Formula
include Language::Python::Virtualenv
desc "Using LLMs to interact with man pages"
url "https://github.com/nikhilkmr300/scanman/archive/refs/tags/1.0.1.tar.gz"
sha256 "93658e02082e9045b8a49628e7eec2e9463cb72b0e0e9f5040ff5d69f0ba06c8"
depends_on "[email protected]"
def install
virtualenv_install_with_resources
bin.install "scanman"
end
test do
# Simply run the program
system "#{bin}/scanman"
end
end
使用安装的 scanman 版本运行应用程序时,无法找到 src 目录中的自定义模块。
ModuleNotFoundError: No module named 'src'
对于为什么会发生这种情况有任何见解吗?
这是我的目录结构(如果有帮助的话):
.
├── requirements.txt
├── scanman
├── scanman.rb
├── setup.py
└── src
├── __init__.py
├── cli.py
├── commands.py
├── manpage.py
├── rag.py
└── state.py
主要可执行文件是
scanman
。这是一个 Python 脚本,可让您使用 LLM 与手册页进行交互。
值得注意的是:
scanman
时,它工作得非常好。/usr/local/Cellar/scanman/1.0.1/libexec/lib/python3.11/site-packages/
中找不到它们。我查看了 repo,发现
scanman
本身就是一个 Python 脚本。
你实际上有两个问题:
packages=find_packages(where="src")
中设置 setup.py
,这会告诉 Setuptools(读取 setup.py
并构建软件包的程序)安装 src
内的所有内容。因此,您的模块
cli.py
、
commands.py
等最终都会安装为顶级模块,您可以通过
import cli
、
import commands
进行访问。这可能不是你想要的。
#!/usr/bin/env python3
受到
PATH
中任何内容的支配,它甚至可能不受 Homebrew 管理,更不用说 Homebrew 为你的包创建的特定 venv 了。
不应该制作一个名为src
的可Pip安装包。你应该努力做到这一点,以便Python可以找到你的代码,而无需任何额外的环境变量或其他混乱。您也不想重写您的代码。我在这里的建议是为了实现所有这些目标。 看起来您已经在尝试制作一个可安装 Pip 的软件包,所以我不会胡乱尝试让您当前的设置正常工作,而是将说明如何以一种不会造成混乱并遵循标准的方式来做到这一点长期存在的约定。
您的文件结构将如下所示:
./
Formula/
scanman.rb
.gitignore
requirements.txt
setup.py
src/
scanman/
__init__.py
__main__.py
cli.py
commands.py
manpage.py
rag.py
state.py
缺少顶级 scanman
脚本是故意的。继续阅读。你的
setup.py
变成:
from setuptools import setup, find_packages
setup(
name="scanman",
description="Using LLMs to interact with man pages",
url="https://github.com/nikhilkmr300/scanman",
author="Nikhil Kumar",
author_email="[email protected]",
license="MIT",
packages=find_packages(where="src"),
install_requires=[
"faiss-cpu",
"langchain",
"langchain-openai",
"langchainhub",
"openai",
"termcolor"
],
entry_points={
"console_scripts": [
"scanman = scanman.__main__:main",
]
},
)
请注意,这与您之前的内容几乎相同。
然后:
src.cli
、
src.manpage
等的导入更改为
scanman.cli
、
scanman.manpage
。
scanman
脚本的内容移至脚本
src/scanman/__main__.py
内的 function,然后删除 shebang 行。 。在您的开发环境(最好是 venv!)中运行
pip install --editable .
。只需运行一次,更新代码时无需重新运行。
src/scanman/__main__.py
脚本将如下所示:
import argparse
import logging
import os
import readline
import sys
from langchain.memory import ConversationBufferWindowMemory
from termcolor import colored
from scanman.cli import prompt
from scanman.commands import Command
from scanman.manpage import Manpage
from scanman.rag import ERROR_MSG, ask, load_retriever
from scanman.state import State
def main():
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.ERROR)
... # all the stuff that was under the if-name-main block
if __name__ == "__main__":
main()
请注意,您不需要同时使用 logging.basicConfig(level=logging.ERROR)
和
logger.setLevel(logging.ERROR)
。日志级别是分层的;无论您在根记录器上设置什么(这就是
logging.basicConfig(level=...)
所做的),都将传播到所有其他记录器,除非您在其他记录器上明确设置不同的级别。作为测试,您应该能够使用开发者环境运行
python -m scanman
和
scanman
:
# Create a fresh venv and install into it:
/opt/homebrew/bin/python3 -m venv ./venv-testing
./venv-testing/bin/pip install -r requirements.txt .
# Both should now work:
./venv-testing/bin/scanman
./venv-testing/bin/python -m scanman
我们喜欢 src/
布局的原因是从 Python 搜索路径中隐藏你的源代码,这样你就 100% 确定你只运行安装在 venv 中的代码版本,不 无论如何当您运行代码时,您的 Git 存储库中恰好有该文件。这提高了您的代码在其他人的机器上运行的机会。 最后,我建议将 Homebrew 公式移至
Formula/
目录,因为这是 Homebrew 中的标准,并且它清楚地表明
scanman.rb
是 Homebrew 公式,而不是另一个项目脚本。
正确的事情!
首先,您只在主脚本中设置日志配置,而不是在库代码中设置。这很好,因为您不应该尝试从库代码内部修改全局应用程序状态。看起来您正在使用
pip-compile
或
pip freeze
来发出
requirements.txt
。这是一个伟大的主意。继续这样做!遗憾的是,我不知道如何让 Homebrew 实际安装该文件中的依赖项,而不是仅使用
setup.py
中声明的包 deps 并进行完整的依赖项解析。您可能需要在 Homebrew 论坛中询问。理想情况下,Homebrew 会运行这样的东西:
$VENV_DIR/bin/pip install -r requirements.txt ./
但这需要它在安装之前克隆您的存储库,而不是简单地通过 Pip 从 Github 直接安装。