当安装的包使用 conda 虚拟环境时,如何使 Python 控制台脚本入口点工作?
Posted
技术标签:
【中文标题】当安装的包使用 conda 虚拟环境时,如何使 Python 控制台脚本入口点工作?【英文标题】:How do you make Python console script entry points work when installed package uses a conda virtual environment? 【发布时间】:2020-02-09 10:51:16 【问题描述】:问题 - 从非虚拟环境转移到 conda 虚拟环境会导致无法识别控制台脚本入口点。
背景 - 我最近尝试让我的 Python 项目使用虚拟环境。在更新到 macOS Catalina 导致我的所有 PyCharm 项目显示无效的解释器错误后,我决定这样做。我想“把一大堆乱七八糟的东西扔在另一个上面会有什么问题?”两天后,我终于可以再次运行脚本了——这是我遇到过的最糟糕的砖墙。我无法在任何地方找到解决方案,所以我正在写我的第一个 SO 问题和我要遵循的解决方案,我想我可能终于有一些值得回馈给这个我已经使用了这么久的网站的东西了。
我的设置
操作系统:macOS Catalina Shell:bash(是的,我在 Catalina 更新后将其改回并抑制了烦人的“zsh is now default”消息) IDE:PyCharm 19.1 专业版 Anaconda:4.4.7 Python:3.7上下文 - 我开发了几个交互的数据科学包,并以可编辑模式在本地安装它们,作为一般做法:
My_Machine:my_package my_user_name$ pip install -e .
我使用带有 setuptools 的 setup.py 文件创建 python 包,使用 PyCharm 构建。在 setup.py 文件中,我定义了如下控制台脚本入口点:
setup.py:
# -*- coding: utf-8 -*-
from setuptools import setup, find_packages
setup(...
name='my_project',
entry_points='console_scripts':['my_entry_name=my_package.scripts.my_python_script:main'
],
...
)
在转移到 conda 虚拟环境之前,多年来我一直通过这样的批处理文件完美地运行脚本:
my_batch_file.command:
#!/bin/bash
cd "$(dirname "$0")" # set the working directory as the command file locations
my_entry_name <script arguments>
但是,在切换到 conda 虚拟环境后,运行命令文件会产生 my_entry_name: command not found
错误。
目前尝试过的事情
已验证并尝试通过which python
终端命令设置使用哪个python。我可以看到默认值为/Users/my_user_name/anaconda3/bin/python
,如果我从项目中的命令提示符执行此操作,我会看到/Users/my_user_name/anaconda3/envs/my_env/bin/python
,反映了预期的环境版本。
检查了/Users/my_user_name/anaconda3/envs/my_env/bin/my_entry_name
中的实际入口点文件,看看shebang行如何指示python版本,即#!/Users/my_user_name/anaconda3/envs/my_env/bin/python
尝试将此 shebang 添加到我的 .command 文件的顶部
多次重新安装我的包,认为入口点可能没有以某种方式正确注册。
经常使用 bash 与 zsh,认为通过 Catalina 更新转换到 zsh,然后再回到 bash 可能会导致问题。
尝试通过从虚拟环境返回来恢复正常运行,但无法让 PyCharm 非虚拟解释器设置再次工作。
查看 $PATH 内容是否存在问题。
阅读大量关于虚拟环境的教程(我发现所有教程都停留在非常基础的内容)
已跟踪有关虚拟环境相关设置工具中的错误的线程
这些努力的许多组合。
这些都不起作用 - 相同的 my_entry_name: command not found
错误。这两天非常沮丧。
【问题讨论】:
不确定我是否在关注。您是否尝试使用两个不同的 Python 解释器重用相同的虚拟环境? 感谢您的回复。不,问题实际上是在虚拟环境中调用包中的脚本,它通常也具有与系统默认值不同的解释器。根据我对这个问题的回答,每次都需要激活环境对我来说并不明显。我想象 python 中的虚拟环境更像是指向特定版本的包和解释器的链接的集合——因此可以在没有任何实际成本的情况下一直处于活动状态。我以为我只需要激活它一次。请在我自己的答案入口点上查看关闭 cmets。 您不必必须激活虚拟环境,永远,甚至一次。假设您在/venv
有一个虚拟环境,那么您可以随时随地拨打/venv/bin/python
或/venv/bin/my_entry_name
,而无需激活虚拟环境,它可以正常工作。如果它不起作用,那么您的设置有问题,必须修复。
@sinoroc 谢谢,这不起作用,因为我使用的是 conda 环境。但是,您为我指出了正确的方向,我发现这可以在没有任何激活的情况下调用 conda env:~/anaconda3/envs/my_env_name/bin/entry_point_name
。虽然这很好,但它仍然消除了入口点提供的大部分简单性 - 即它消除了了解被调用脚本的位置、语言或安装方法的需要。无论如何要在系统范围内注册入口点(因为它通常可以在没有环境的情况下工作),而不是特定于环境?
我添加了一个新问题:***.com/questions/58436993/… 来解决入口点的使用方式,这与这里的主要问题完全不同。
【参考方案1】:
经过两天的努力,我找到了一个解决方案,我将在下面发布,但是我非常想听听 cmets、替代方案,以及直截了当的“你是个白痴”的教育。
似乎需要两个元素:
指示要用作与虚拟环境相关联的 Python 版本(我曾尝试过但未成功)。 激活虚拟环境。我将我的 .command 文件更改为此,在指定它之前和之后添加了几个不必要的 python 版本验证检查以及相关的文本输出:
my_batch.command:
#!/usr/bin/env bash
echo "VERIFY: Python version BEFORE activating virtual environment:"
which python
echo "Activating conda virtual environment..."
source activate my_env_name
echo "Setting python version to use in this environment..."
#!/Users/my_username/anaconda3/envs/my_env_name/bin/python
echo "VERIFY: Python version AFTER activating virtual environment:"
which python
cd "$(dirname "$0")" # set the working directory as the command file locations
echo "RUN THE SCRIPT:"
my_entry_name <script arguments>
我缺少的关键行是source activate my_env_name
。我验证删除它会导致失败,并且必须每次都包含它,而不仅仅是一次,因此包含在我的 .command 文件中。
我也不清楚有多条 shebang 线是可以的,但这很好用。
我很高兴能再次正常工作,但我不得不承认我很沮丧,因为架构不能让我的入口点正常工作,期间没有这种笨拙。拥有入口点的原因是允许脚本的使用者轻松调用它,而不必关心脚本的位置和安装方式等细节。虚拟环境的使用似乎消除了这些入口点的便利。我完全赞成使用虚拟环境的好处,但是有什么方法可以让我的蛋糕也吃掉吗?理想情况下,调用入口点将激活虚拟环境并知道要使用哪个版本的 python。有没有更好的方法来做到这一点?
【讨论】:
【参考方案2】:您不必激活 Python 虚拟环境,甚至一次都不需要。假设您在 /venv
有一个虚拟环境,那么您可以调用 /venv/bin/python
或 /venv/bin/my_entry_name
随时随地无需激活虚拟环境,它应该可以正常工作。如果它不起作用,那么您的设置有问题,必须修复。
当您使用 setuptools console_scripts
入口点时,虚拟环境中 Python 解释器的完整路径被硬编码在 shebang,所以这应该是不可能的。
更新
我的错,我没有注册您正在使用 conda 虚拟环境。我不知道这些是如何工作的,也许我的观点仍然成立,但可能不是。
【讨论】:
是的,既然我问了这个,我意识到我可以通过将python版本放在setup.py文件顶部的shebang中来解决一半的问题。我已经通过:#! ~/anaconda3/envs/my_env_name/bin/python
尝试过,但它不起作用。我需要使用相对于用户主路径的路径,因为它对于脚本的使用者来说会有所不同 - 即不是#!/Users/my_user_name/anaconda3/envs/my_env_name/bin/python
。【参考方案3】:
在进一步输入和实验后修改了答案。我发现了两个选项,它们都需要知道定义入口点的实际文件在哪里。对于 conda 虚拟环境,它将在这里:
/anaconda3/envs/my_env_name/bin/entry_point_name
调用该文件无需指定python版本或激活环境。我找到了两种方法来做到这一点:
选项 1 - 感谢@sinoroc,他首先指出如果正确调用入口点,则不需要激活虚拟环境。对于 conda 虚拟环境,这将是:
my_batch_file.command
#!/bin/bash
cd "$(dirname "$0")" # set the working directory as the command file locations
~/anaconda3/envs/my_env_name/bin/entry_point_name <my script args>
对于其他类型的虚拟环境,您只需调整路径详细信息即可到达入口点文件。
选项 2 - 如果您将入口点文件的副本放在 Anaconda 主 bin 中:
/anaconda3/bin/my_entry_name
你不需要指定路径,让你调用入口点就像他们应该工作一样,恕我直言 - 即屏蔽脚本用户的语言和安装细节:
my_batch_file.command
#!/bin/bash
cd "$(dirname "$0")" # set the working directory as the command file locations
entry_point_name <my script args>
由于这个手动复制的步骤不够优雅,所以我发了一个关于如何更好地做到这一点的问题:How do you make an entry point to a script in a virtual environment available system-wide?
【讨论】:
以上是关于当安装的包使用 conda 虚拟环境时,如何使 Python 控制台脚本入口点工作?的主要内容,如果未能解决你的问题,请参考以下文章