Python entry points
Code version: vLLM @ 3e44c0d · 2026-10-22
In one sentence
An entry point is a named pointer to a Python object in the metadata of an installed package; other code finds and loads it.
The idea#
A name, a group and a target#
When you install a Python package, the installer writes a small metadata folder next to the code. One file in that folder can list entry points. Each entry point has three parts:
- a group, for example
vllm.general_pluginsorconsole_scripts(an entry-point group); - a name inside the group, for example
lora_filesystem_resolver; - a value of the form
module.path:attribute, the object to load.
Any program can ask: "Which entry points of group X are installed?" It gets the names and values without an import of the packages. Only when it calls load() on one entry point does Python import that module.
Two uses in vLLM#
- The
vllmcommand. A console script is an entry point in the groupconsole_scripts. The installer makes a small program calledvllmthat callsvllm.entrypoints.cli.main:main(pyproject.toml:42-43). - Plugins. vLLM reads its own groups. A package that you install next to vLLM adds an entry point to such a group, and vLLM calls it at start-up. vLLM itself uses this for its two built-in LoRA resolvers (
pyproject.toml:45-47).
So the plugin system of vLLM needs no configuration file. "Install the package" is enough. The page How vLLM finds and loads plugins explains when each process loads each group.
An everyday comparison#
Think of the directory board in the hall of an office building. Each company writes its name and its room number on the board when it moves in. A visitor reads the board and then goes to the room. The visitor does not open every door to find the company.
The comparison stops here: a visitor can only read the board. load() also "opens the door": it imports the module, and the import runs the module's top-level code.
The picture#
Read panel 1 at install time and panel 2 at run time. The green box is the only link between them: a text file in the installed metadata. That is why a plugin works without a change to vLLM: the installer writes the file, and vLLM reads it.
In vLLM#
This is a basics page, so this part gives only pointers. The concept page How vLLM finds and loads plugins and the module page plugins/__init__.py: the plugin loaders explain the loading code in full.
- vLLM's own entry points. The
pyproject.tomlof vLLM declares the console script and two general plugins:[project.scripts] vllm = "vllm.entrypoints.cli.main:main" [project.entry-points."vllm.general_plugins"] lora_filesystem_resolver = "vllm.plugins.lora_resolvers.filesystem_resolver:register_filesystem_resolver" lora_hf_hub_resolver = "vllm.plugins.lora_resolvers.hf_hub_resolver:register_hf_hub_resolver" - A plugin package in the tests. The test package
vllm_add_dummy_modeldeclares its entry point withsetup.py(tests/plugins/vllm_add_dummy_model/setup.py:6-13). Its function registers extra model classes (tests/plugins/vllm_add_dummy_model/vllm_add_dummy_model/__init__.py:7-12). - The loader.
load_plugins_by_group()asksimportlib.metadata.entry_points(group=...), filters by name, and callsload()on each entry point (vllm/plugins/__init__.py:36-74).
Deep dive#
The sections below give the full detail. Open them one at a time, or use Open all deep dives at the top of the page.
Deep diveDeclare an entry point: pyproject.toml or setup.py
pyproject.toml. The [project] table of the packaging standard has two places for entry points:
[project.scripts]: each key becomes a console script (pyproject.toml:42-43). The installer writes it into the groupconsole_scripts.[project.entry-points."<group>"]: any other group. The group name has dots, so it needs quotes (pyproject.toml:45-47).
setup.py. Older packages pass a dictionary to setup(): group name → list of "name = module:attr" strings. One package can fill several groups. The dummy platform plugin of the tests fills two groups at once (tests/plugins/vllm_add_dummy_platform/setup.py:6-18):
setup(
name="vllm_add_dummy_platform",
version="0.1",
packages=["vllm_add_dummy_platform"],
entry_points={
"vllm.platform_plugins": [
"dummy_platform_plugin = vllm_add_dummy_platform:dummy_platform_plugin" # noqa
],
"vllm.general_plugins": [
"dummy_custom_ops = vllm_add_dummy_platform:register_ops"
],
},
)What the installer writes. In the installed <name>-<version>.dist-info/ folder, the file entry_points.txt has one INI section per group. For vLLM it looks like this (the file is not in the repo; it comes from the build):
[console_scripts]
vllm = vllm.entrypoints.cli.main:main
[vllm.general_plugins]
lora_filesystem_resolver = vllm.plugins.lora_resolvers.filesystem_resolver:register_filesystem_resolver
lora_hf_hub_resolver = vllm.plugins.lora_resolvers.hf_hub_resolver:register_hf_hub_resolver
An editable install (pip install -e .) also writes this file. So when you add or rename an entry point, install the package again. A change to the code of the target needs no new install.
Deep diveRead entry points with importlib.metadata: a tiny example
vLLM needs Python 3.11 or newer (pyproject.toml:34). From Python 3.10, importlib.metadata.entry_points(group=...) returns only the entry points of one group. Each item is an EntryPoint with these attributes:
| Attribute | Example |
|---|---|
ep.group | "vllm.general_plugins" |
ep.name | "lora_filesystem_resolver" |
ep.value | "vllm.plugins.lora_resolvers.filesystem_resolver:register_filesystem_resolver" |
ep.module, ep.attr | the parts before and after the colon |
ep.load() | imports ep.module, then returns the attribute ep.attr of it |
A tiny plugin package (example files, not in the repo). Two files are enough:
# my_vllm_plugin/pyproject.toml
[project]
name = "my-vllm-plugin"
version = "0.1"
[project.entry-points."vllm.general_plugins"]
hello = "my_vllm_plugin:register"
# my_vllm_plugin/my_vllm_plugin/__init__.py
def register():
print("hello from my_vllm_plugin")
Install it with pip install -e my_vllm_plugin. Then read the group from any Python process:
from importlib.metadata import entry_points
eps = entry_points(group="vllm.general_plugins")
for ep in eps:
print(ep.name, "->", ep.value)
ep = next(ep for ep in eps if ep.name == "hello")
func = ep.load() # imports my_vllm_plugin now
func() # prints "hello from my_vllm_plugin"
Install no other plugin of this group. Then the loop prints three lines: hello and the two LoRA resolvers of vLLM. The order of the lines depends on the order of the packages on sys.path; do not depend on it.
Deep diveHow vLLM reads a group, line by line
def load_plugins_by_group(group: str) -> dict[str, Callable[[], Any]]:
"""Load plugins registered under the given entry point group."""
from importlib.metadata import entry_points
allowed_plugins = envs.VLLM_PLUGINS
discovered_plugins = entry_points(group=group)
if len(discovered_plugins) == 0:
logger.debug("No plugins for group %s found.", group)
return {}
# Check if the only discovered plugin is the default one
is_default_group = group == DEFAULT_PLUGINS_GROUP
# Use INFO for non-default groups and DEBUG for the default group
log_level = logger.debug if is_default_group else logger.info
log_level("Available plugins for group %s:", group)
for plugin in discovered_plugins:
log_level("- %s -> %s", plugin.name, plugin.value)
if allowed_plugins is None:
log_level(
"All plugins in this group will be loaded. "
"Set `VLLM_PLUGINS` to control which plugins to load."
)
plugins = dict[str, Callable[[], Any]]()
for plugin in discovered_plugins:
if allowed_plugins is None or plugin.name in allowed_plugins:
if allowed_plugins is not None:
log_level("Loading plugin %s", plugin.name)
try:
func = plugin.load()
plugins[plugin.name] = func
except Exception:
logger.exception("Failed to load plugin %s", plugin.name)
return plugins- Line 40.
VLLM_PLUGINSis a list of names, orNonewhen the variable is not set (vllm/envs.py:1163-1167). - Line 42.
entry_points(group=group)reads the metadata of all installed packages. Nothing is imported yet. - Lines 43-45. An empty group returns an empty dict.
- Lines 52-54. The loader logs each
ep.nameandep.value: at DEBUG forvllm.general_plugins, at INFO for other groups. - Line 64. The filter compares
ep.name, not the package name, withVLLM_PLUGINS. - Lines 68-72.
plugin.load()imports the module. An exception is logged withlogger.exception, and the loop goes on with the next plugin. - Line 74. The result maps each name to the loaded object. The caller decides what to do with it. For general plugins,
load_general_plugins()calls each function once per process (vllm/plugins/__init__.py:77-90).
The groups. vLLM reads five groups through this function (vllm/plugins/__init__.py:16-30). A sixth group, vllm.logits_processors, has its own loader that calls entry_points() directly (vllm/v1/worker/gpu/sample/logits_processor/loader.py:46). Which process loads which group, when, and what the loaded object must be, is the topic of How vLLM finds and loads plugins. This page does not repeat it.
Deep diveThe console script vllm, and other uses of package metadata
For each console script, the installer writes a small executable into the bin/ folder of the environment (Scripts\ on Windows). It imports vllm.entrypoints.cli.main and calls main(). So vllm serve ... and python -m vllm.entrypoints.cli.main serve ... run the same function (vllm/entrypoints/cli/main.py:122-123).
The same metadata gives the version. vllm --version prints importlib.metadata.version("vllm"): the version of the installed distribution, not a constant in the code (vllm/entrypoints/cli/main.py:92-97).
Worked example. The value vllm.entrypoints.cli.main:main splits at the colon. The module part is vllm.entrypoints.cli.main, and the attribute part is main. So ep.load() does the same as importlib.import_module("vllm.entrypoints.cli.main").main. A value can also name a dotted attribute, for example pkg.mod:Class.method; then load() follows each dot.
What can go wrong#
- The plugin is installed in another environment.
entry_points()reads only the packages on thesys.pathof the running interpreter. Install the plugin with the samepipas vLLM. - The metadata is old. You renamed the entry point or the function, but did not install the package again. The old name is still in
entry_points.txt, andload()fails. - A wrong value fails quietly. When
load()raises, vLLM logs the exception and skips the plugin (vllm/plugins/__init__.py:68-72). The server starts without your plugin. Read the log for "Failed to load plugin". VLLM_PLUGINSuses entry-point names. It matchesep.name, for exampleregister_dummy_model, not the package namevllm_add_dummy_model(vllm/plugins/__init__.py:64,tests/plugins/vllm_add_dummy_model/setup.py:11).- Heavy imports.
load()runs the top-level code of the module, in every process that loads the group. Keep the module light and do the work inside the function. - The function runs more than once. A general plugin runs in several processes. The docstring says that plugins "should be designed in a way that they can be loaded multiple times" (
vllm/plugins/__init__.py:78-80). The test plugin checks before it registers a model (tests/plugins/vllm_add_dummy_model/vllm_add_dummy_model/__init__.py:11-12).
Why it matters. Every vLLM plugin starts as an entry point. When a plugin does not load, the reason is almost always one of the points above.
Check yourself#
- 1What are the three parts of an entry point?easy
Show answer
A group (for example
vllm.general_plugins), a name (for examplelora_filesystem_resolver) and a valuemodule:attr(pyproject.toml:45-47). - 2Which function and which group make theeasy
vllmcommand?Show answer
vllm.entrypoints.cli.main:main, declared in[project.scripts](pyproject.toml:42-43). The installer puts it into the groupconsole_scriptsand writes avllmexecutable. - 3Doesmedium
entry_points(group="vllm.general_plugins")import the plugin modules?Show answer
No. It reads metadata only. The import happens in
plugin.load()(vllm/plugins/__init__.py:42,vllm/plugins/__init__.py:69). That is why vLLM can filter by name before any plugin code runs (vllm/plugins/__init__.py:64). - 4You sethard
VLLM_PLUGINS=vllm_add_dummy_modeland install the test package. The model is not registered, and there is no error. Why?Show answer
The filter compares entry-point names (
vllm/plugins/__init__.py:64). The name in the test package isregister_dummy_model;vllm_add_dummy_modelis the package name (tests/plugins/vllm_add_dummy_model/setup.py:7-11). So the loader skips the plugin. SetVLLM_PLUGINS=register_dummy_model. - 5Your plugin module has a syntax error. What happens whenhard
vllm servestarts, and where do you see it?Show answer
plugin.load()raises during the import. The loader catches it, logs "Failed to load plugin ..." with the traceback, and goes on (vllm/plugins/__init__.py:68-72). The server starts without the plugin, so you see the problem only in the log.