检查我的应用程序是否在开发/可编辑模式下运行

6

我的Python应用程序可以按照常规方式安装,或使用pip以开发/可编辑模式安装,如下所示:

virtualenv my_app
source my_app/bin/activate
pip install -e my_app

我该如何编写一个函数来检查我的虚拟环境,并检查我的应用程序是否在开发/可编辑模式下运行?

sys 模块中是否有任何“标志”可以用于此目的?

动机:在开发模式和生产模式下具有不同的配置。

编辑:我正在比较 虚拟环境 目录。

import os
import sys

import pkg_resources

main_pkg = 'my_app'
package_dir = pkg_resources.resource_filename(main_pkg, '__init__.py')
virtualenv_dir = os.path.dirname(os.path.dirname(sys.executable))
common_path = os.path.commonprefix([package_dir, virtualenv_dir])
is_dev_mode = not common_path.startswith(virtualenv_dir)

我检查package_dir是否是virtualenv_dir的子目录:如果不是子目录,则表示我处于开发模式。

编辑2:

有没有更可靠的解决方案?

我想知道环境变量中是否有一个数据/标志可以清楚地告诉我我的应用程序正在以开发模式运行。

如果另一个依赖项也处于开发模式,会发生什么?


请注意,Python开发模式与本问题所询问的内容不同。 - Flimm
3个回答

5

使用pip提供的代码,我们可以确定这个问题:

import pip
import pkg_resources

# I've done `pip install -e .` for a git repo I'm working in inside
# a virtualenv
distributions = {v.key: v for v in pkg_resources.working_set}
# >>> distribution
# pre-commit 0.9.3 (/home/asottile/workspace/pre-commit)
distribution = distributions['pre-commit']

# Below is approximately how `pip freeze` works, see the end of
# the answer for a simpler approach, still using pip

# Turn into a pip FrozenRequirement (I'm using pip 9.0.1, it may
# be different for your version)
# I've passed an empty list for the second argument (dependency_links)
# I don't think it's necessary?
frozen_requirement = pip.FrozenRequirement.from_dist(distribution, [])

# Query whether the requirement is installed editably:
print(frozen_requirement.editable)

这个神奇的功能来自 pip (pip.utils) 中的一个小函数:
def dist_is_editable(dist):
    """Is distribution an editable install?"""
    for path_item in sys.path:
        egg_link = os.path.join(path_item, dist.project_name + '.egg-link')
        if os.path.isfile(egg_link):
            return True
    return False

在这里,dist是一个pkg_resources分发包(就像我们之前获取的)。当然,您也可以直接使用dist_is_editable函数,而不必通过FrozenRequirement进行操作。
# With `distribution` as above:
from pip.utils import dist_is_editable
print(dist_is_editable(distribution))  # True in my case ;)

听起来不错;-) pkg_resources.working_set 的键是软件包名称 (import pkg_name) 还是库的名称(比如 name="lib_name" in setup.py)? - Laurent LAPORTE
键将最终成为“规范化”的软件包名称(因此对于yaml,您将寻找pyyaml,对于pre_commit,您将寻找pre-commit)。 规范化规则遵循https://www.python.org/dev/peps/pep-0440/(PEP440)(尽管通常最终是`.replace('_', '-').lower()`)。 - anthony sottile
从pip文档中-您不应以这种方式使用pip的内部API。而且这些信息已经过时了,现在一个分发是否可编辑可以记录在direct_url.json中,而不是一个egg-link。 - wim
@wim 当然,这是在不同的时代写的,现在已经过去了5年 :) - anthony sottile
是的,尽管当时pip也没有任何公共API(参考:#4696)。更重要的是,最好更新答案,提供一种目前正在工作/受支持的方法。 - wim
这个问题是在这篇文章发布一年后出现的,其中的内容和精神仍然有效(难点在于定位蛋链接/蛋信息)。direct_url.json 部分是后来才出现的,除非您能保证 pip 版本,否则不太可能起作用。 - anthony sottile

3

顶部解决方案需要更新为pip的更高版本,其中FrozenRequirement类在pip的名称空间中不再可访问。

我在相关行中使用了这个解决方法。

from pip._internal.operations.freeze import FrozenRequirement 
frozen_requirement = FrozenRequirement.from_dist(distribution)

即使使用pip==22.3.1,这也不再起作用了。 - gebbissimo

1

我不确定是否有可靠的方法来确定这一点。实际上,我建议您不要测试这样的路径,因为您可能会在各种操作系统或环境中遇到不同的特殊情况。

以下是我推荐的几个替代方案:

1)如果这是一个命令行实用程序,我建议允许通过命令行标志加载自定义配置文件:

from argparse import ArgumentParser
import sys
import json

parser = ArgumentParser(description='...')
parser.add_argument('-c', '--config', default='config.json')

def main(argv):
    args = parser.parse_args(argv)
    print('loading config file:', args.config)
    with open(args.config, 'r') as config:
        config = json.loads(config.read())
    print('loaded config', config) 
    # TODO do something with the config

if __name__ == '__main__':
    main(sys.argv[1:])

运行命令: python3 test1.py -c config-dev.json

2) 如果这不是一个CLI应用程序,您可以通过使用环境变量来实现类似的功能:

import os
import json

os.environ.get('CONFIG', 'config.json')

def main():
    config_file = os.environ.get('CONFIG', 'config.json')
    print('loading config file:', config_file)
    with open(config_file, 'r') as config:
        config = json.loads(config.read())
    print('loaded config', config) 
    # TODO do something with the config

if __name__ == '__main__':
    main()

使用以下命令运行:CONFIG=config-dev.json python3 test2.py 或者:

export CONFIG=config-dev.json
python3 test2.py

你也可以编写一个 shell 脚本来帮助设置开发环境,我们称之为 customenv

source env/bin/activate
export CONFIG=config-dev.json

然后您可以使用此文件来激活开发环境:

source customenv

3)如果您真的希望在代码中为开发环境指定一个特殊情况,您也可以通过环境变量来指定:

import os
is_dev_mode = 'MY_APP_DEV' in os.environ and os.environ['MY_APP_DEV'] == '1'
if is_dev_mode:
    print('dev mode!')

使用 MY_APP_DEV=1 python3 test3.py 命令运行,或者:

export MY_APP_DEV=1
python3 -m test3.py

4) 如需更多自定义:

import os
app_mode = os.environ.get('MY_APP_MODE', 'prod')
print(app_mode)

我同意你的观点,但这不是我想要的。很抱歉,但我真的需要检查一下我是否处于“开发”模式。 - Laurent LAPORTE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接