You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在运行时添加入口点以被importlib.metadata.entry_points()识别?

运行时添加入口点及让importlib.metadata检测到的方法

Great question! Let’s break this down clearly, since there’s a critical difference between just adding an entry point for your own runtime use and making it visible to importlib.metadata.entry_points()—plus, we’ll steer clear of the deprecated pkg_resources methods from the older questions you mentioned.


1. 如何在运行时添加入口点(仅自用,无需被importlib.metadata检测)

If you just need to create and use an entry point dynamically in your code (without needing other parts of your program to discover it via entry_points()), this is straightforward. You can directly create an EntryPoint object and load it:

from importlib.metadata import EntryPoint

# 定义动态入口点:名称、模块对象路径、所属分组
my_entry_point = EntryPoint(
    name="my_custom_tool",
    value="my_project.utils:my_utility_function",
    group="my_custom_group"
)

# 加载并执行入口点指向的对象
loaded_function = my_entry_point.load()
loaded_function()  # 运行你的目标函数

这种方式完全适配临时使用场景,不需要让入口点出现在全局注册表中。


2. 如何让importlib.metadata.entry_points()找到运行时添加的入口点

importlib.metadata.entry_points()默认只会读取已安装包的元数据中的入口点。要让动态添加的入口点被它检测到,你需要通过猴子补丁修改函数,或者修改它的内部缓存(注意:这些是变通方案,因为原API并非为运行时修改设计)。

方法一:猴子补丁entry_points()函数(稳定、推荐)

这种方法包装原始的entry_points()函数,在每次调用时注入自定义入口点:

from importlib.metadata import entry_points as original_entry_points
from importlib.metadata import EntryPoint

# 定义你要添加的动态入口点
new_entry = EntryPoint(
    name="my_dynamic_script",
    value="my_project.cli:main",
    group="console_scripts"
)

def patched_entry_points(**kwargs):
    # 获取原始的入口点结果
    original_results = original_entry_points(**kwargs)
    
    # 处理指定分组的请求
    if "group" in kwargs:
        target_group = kwargs["group"]
        if target_group == new_entry.group:
            # 将原始结果转为列表(通常是不可变的)并添加我们的入口点
            return list(original_results) + [new_entry]
        return original_results
    else:
        # 处理全量入口点集合:转为字典,添加入口点后再包装回去
        entry_groups = {g: list(original_results.select(group=g)) for g in original_results.groups}
        if new_entry.group not in entry_groups:
            entry_groups[new_entry.group] = []
        entry_groups[new_entry.group].append(new_entry)
        
        # 转回EntryPoints对象(兼容Python 3.10+)
        try:
            from importlib.metadata import EntryPoints
            return EntryPoints(entry_groups)
        except ImportError:
            # 兼容旧Python版本(返回字典格式)
            return entry_groups

# 用补丁函数替换原始的entry_points函数
import importlib.metadata
importlib.metadata.entry_points = patched_entry_points

# 测试效果!
console_entries = importlib.metadata.entry_points(group="console_scripts")
print([ep.name for ep in console_entries])  # 列表中会出现"my_dynamic_script"

方法二:修改内部缓存(更底层、依赖版本)

如果你需要更直接的方式(警告:依赖importlib.metadata的内部实现,不同Python版本可能有变化),可以编辑内部入口点缓存:

from importlib.metadata import EntryPoint
import importlib.metadata

new_entry = EntryPoint(
    name="my_dynamic_plugin",
    value="my_project.plugins:custom_plugin",
    group="my_plugins"
)

# 访问并修改内部入口点缓存(以Python 3.10+为例)
if hasattr(importlib.metadata, "_entry_points"):
    entry_cache = importlib.metadata._entry_points
    if new_entry.group not in entry_cache:
        entry_cache[new_entry.group] = []
    entry_cache[new_entry.group].append(new_entry)
    
    # 清除已缓存的入口点结果,确保修改生效
    if hasattr(importlib.metadata, "_cached_entry_points"):
        del importlib.metadata._cached_entry_points

关于已弃用的pkg_resources

你提到的旧问题使用的是pkg_resources,它现在已被弃用,推荐使用importlib.metadata替代。新代码请避免使用pkg_resources,上述方法适配Python 3.8+的现代版本。

内容的提问来源于stack exchange,提问作者flying sheep

火山引擎 最新活动