mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-24 03:21:47 +08:00
fix: align loader tests with renamed manifest API and strip ticket refs
The merge of feat/extension-validate brought in the LfxCompat / compat rename, but the loader test fixtures still used LangflowCompat / bundle_api and failed at the manifest layer. Update conftest.py + test_load_extension.py to the post-rename API. Strip 17 LE-XXXX ticket references from loader source files, errors.py loader-specific comments, and the four loader test docstrings, matching the convention applied to LE-1014. Descriptive prose preserved. Promote _BUNDLE_NAME_RE to BUNDLE_NAME_RE on the manifest module so the loader's orchestrator no longer reaches across modules into a private name.
This commit is contained in:
@ -57,16 +57,17 @@ ERROR_CODES: frozenset[str] = frozenset(
|
||||
"import-star-disallowed",
|
||||
"top-level-io-disallowed",
|
||||
"execute-imports-failed",
|
||||
# Loader-specific codes (LE-1015)
|
||||
# Loader-specific codes
|
||||
"module-import-failed",
|
||||
"duplicate-component-name",
|
||||
"duplicate-inline-bundle",
|
||||
"inline-bundle-name-invalid",
|
||||
}
|
||||
)
|
||||
# NOTE: ``duplicate-distribution`` will be added by LE-1022 (installed-pkg
|
||||
# discovery) once there is a startup flow + events surface that can actually
|
||||
# emit it. We hold the line that every registered code must have a producer.
|
||||
# NOTE: ``duplicate-distribution`` will be added with the installed-package
|
||||
# discovery flow once there is a startup path + events surface that can
|
||||
# actually emit it. We hold the line that every registered code must have a
|
||||
# producer.
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
"""Single-Bundle loader for the Langflow Extension System (LE-1015).
|
||||
"""Single-Bundle loader for the Langflow Extension System.
|
||||
|
||||
This package turns a directory tree on disk (an Extension or a loose
|
||||
LANGFLOW_COMPONENTS_PATH entry) into a list of :class:`LoadedComponent`
|
||||
@ -45,11 +45,11 @@ Internal layout (all underscore-prefixed; not part of the public surface):
|
||||
- ``_plugins`` -- manifest-first precedence over ``langflow.plugins``;
|
||||
installed-distribution discovery primitives.
|
||||
|
||||
LE-1022 will reuse ``_plugins.installed_extension_roots`` for the read-only
|
||||
@official slot at server startup; LE-1018 will reuse ``_orchestrator`` plus
|
||||
the discovery layer to drive atomic-swap reload. Splitting the loader into
|
||||
small files now keeps each follow-on ticket touching one banner-section at
|
||||
a time.
|
||||
A future installed-package / seed-dir discovery flow will reuse
|
||||
``_plugins.installed_extension_roots`` for the read-only @official slot at
|
||||
server startup, and atomic-swap reload will reuse ``_orchestrator`` plus the
|
||||
discovery layer. Splitting the loader into small files now keeps each
|
||||
follow-on touching one banner-section at a time.
|
||||
"""
|
||||
|
||||
from lfx.extension.loader._orchestrator import (
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
"""Component-subclass detection for the extension loader (LE-1015).
|
||||
"""Component-subclass detection for the extension loader.
|
||||
|
||||
After ``_discovery`` imports a bundle module, this module is responsible
|
||||
for picking the Component subclasses out of the resulting namespace. The
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
"""Filesystem walk + module import for the extension loader (LE-1015).
|
||||
"""Filesystem walk + module import for the extension loader.
|
||||
|
||||
This module owns the "find files / build module names / actually import"
|
||||
half of the loader pipeline. It deliberately knows nothing about Component
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
"""Load orchestration: turn a directory tree into a :class:`LoadResult` (LE-1015).
|
||||
"""Load orchestration: turn a directory tree into a :class:`LoadResult`.
|
||||
|
||||
This module wires the discovery + detection layers together and exposes the
|
||||
two public entry points the rest of Langflow consumes:
|
||||
@ -37,7 +37,7 @@ from lfx.extension.loader._types import (
|
||||
LoadResult,
|
||||
)
|
||||
from lfx.extension.manifest import (
|
||||
_BUNDLE_NAME_RE,
|
||||
BUNDLE_NAME_RE,
|
||||
BundleRef,
|
||||
ExtensionManifest,
|
||||
load_manifest,
|
||||
@ -306,7 +306,7 @@ def load_extension(
|
||||
|
||||
# Inline-bundle metadata, optionally provided as ``bundle.json`` at the
|
||||
# bundle's root. Only ``version`` is read in v0; ``name`` is derived from
|
||||
# the directory name and validated against _BUNDLE_NAME_RE so that the
|
||||
# the directory name and validated against BUNDLE_NAME_RE so that the
|
||||
# namespaced ID is well-formed. This is intentionally a tiny shape; full
|
||||
# manifest support belongs at the @official slot.
|
||||
_INLINE_BUNDLE_DEFAULT_VERSION = "0.0.0"
|
||||
@ -382,7 +382,7 @@ def discover_inline_bundles(
|
||||
continue
|
||||
result = LoadResult(slot=SLOT_EXTRA, source_path=child)
|
||||
|
||||
if not _BUNDLE_NAME_RE.match(name):
|
||||
if not BUNDLE_NAME_RE.match(name):
|
||||
result.errors.append(
|
||||
ExtensionError(
|
||||
code="inline-bundle-name-invalid",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
"""Manifest-first precedence over ``langflow.plugins`` entry-points (LE-1015).
|
||||
"""Manifest-first precedence over ``langflow.plugins`` entry-points.
|
||||
|
||||
This is the bridge between the new manifest-based loader and the legacy
|
||||
``langflow.plugins`` entry-point system that third parties already use to
|
||||
@ -9,9 +9,9 @@ to avoid double registration. Non-component entry-points (services,
|
||||
routes) on the same distribution are unaffected -- the caller's loop is
|
||||
responsible for that distinction.
|
||||
|
||||
LE-1022 will use the same primitives at server startup to drive the
|
||||
read-only @official slot (installed-package + seed-dir discovery). The
|
||||
helpers live in their own module so that downstream module gets a stable
|
||||
The installed-package / seed-dir discovery flow consumes the same
|
||||
primitives at server startup to drive the read-only @official slot. The
|
||||
helpers live in their own module so that downstream consumer gets a stable
|
||||
import surface to reach for.
|
||||
"""
|
||||
|
||||
@ -114,9 +114,9 @@ def installed_extension_roots(
|
||||
|
||||
# Two distributions with the same canonical name (rare but possible in
|
||||
# broken venvs) are resolved by keeping the lexicographically-first
|
||||
# manifest path for determinism. LE-1022 will surface the conflict as
|
||||
# a typed warning when it owns the startup-time discovery flow; in this
|
||||
# primitive we only return the resolved mapping, never an error.
|
||||
# manifest path for determinism. The startup-time discovery flow will
|
||||
# surface the conflict as a typed warning when it lands; this primitive
|
||||
# only returns the resolved mapping, never an error.
|
||||
found: dict[str, tuple[Path, str]] = {}
|
||||
for dist in distributions:
|
||||
manifest_path = _distribution_manifest_path(dist)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
"""Public dataclasses and slot constants for the extension loader (LE-1015).
|
||||
"""Public dataclasses and slot constants for the extension loader.
|
||||
|
||||
Kept module-level so consumers (events pipeline, registry, tests) can import
|
||||
them without paying the cost of dragging in the discovery / detection /
|
||||
@ -41,10 +41,10 @@ class LoadedComponent:
|
||||
"""A successfully loaded Component class plus its registry coordinates.
|
||||
|
||||
Frozen so callers can place these in sets / dicts and emit them across
|
||||
the events pipeline (LE-1017) without worrying about mutation.
|
||||
the events pipeline without worrying about mutation.
|
||||
|
||||
The :attr:`namespaced_id` is the canonical address used by saved flows
|
||||
after the migration table (LE-1020) rewrites legacy references.
|
||||
after the migration table rewrites legacy references.
|
||||
"""
|
||||
|
||||
extension_id: str
|
||||
|
||||
@ -82,7 +82,7 @@ _EXTENSION_ID_RE: re.Pattern[str] = re.compile(r"^[a-z][a-z0-9-]{1,63}$")
|
||||
"""Extension IDs are lowercase, hyphenated, must start with a letter, 2-64 chars.
|
||||
Mirrors the npm-package / PyPI normalization rules."""
|
||||
|
||||
_BUNDLE_NAME_RE: re.Pattern[str] = re.compile(r"^[a-z][a-z0-9_]{1,63}$")
|
||||
BUNDLE_NAME_RE: re.Pattern[str] = re.compile(r"^[a-z][a-z0-9_]{1,63}$")
|
||||
"""Bundle names use snake_case so they can be addressed in the registry as
|
||||
``ext:<bundle>:<Class>@<slot>`` without quoting."""
|
||||
|
||||
@ -191,7 +191,7 @@ class BundleRef(BaseModel):
|
||||
|
||||
name: StrictStr = Field(
|
||||
...,
|
||||
pattern=_BUNDLE_NAME_RE.pattern,
|
||||
pattern=BUNDLE_NAME_RE.pattern,
|
||||
description=(
|
||||
"Bundle name; addressable as ext:<name>:<Class>@<slot>. "
|
||||
"Lowercase snake_case, starting with a letter, 2-64 chars."
|
||||
|
||||
@ -27,7 +27,7 @@ _BASE_MANIFEST: dict = {
|
||||
"id": "lfx-pilot",
|
||||
"version": "1.2.3",
|
||||
"name": "Pilot Bundle",
|
||||
"lfx": {"bundle_api": [1]},
|
||||
"lfx": {"compat": ["1"]},
|
||||
"bundles": [{"name": "pilot", "path": "components"}],
|
||||
}
|
||||
|
||||
@ -133,7 +133,7 @@ def make_installed_extension(parent: Path, distribution_name: str) -> FakeDist:
|
||||
"id": distribution_name,
|
||||
"version": "1.0.0",
|
||||
"name": distribution_name,
|
||||
"lfx": {"bundle_api": [1]},
|
||||
"lfx": {"compat": ["1"]},
|
||||
"bundles": [{"name": distribution_name.replace("-", "_"), "path": "components"}],
|
||||
}
|
||||
manifest_file = pkg_dir / "extension.json"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
"""Tests for ``discover_inline_bundles`` (LANGFLOW_COMPONENTS_PATH; LE-1015).
|
||||
"""Tests for ``discover_inline_bundles`` (LANGFLOW_COMPONENTS_PATH).
|
||||
|
||||
Covers the @extra-slot AC items:
|
||||
- each immediate subfolder of every path is one Bundle
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
"""Tests for ``load_extension`` -- happy path and failure modes (LE-1015).
|
||||
"""Tests for ``load_extension`` -- happy path and failure modes.
|
||||
|
||||
Covers the AC items for the @official slot:
|
||||
- single-Bundle happy path + identity tuple population
|
||||
@ -121,7 +121,7 @@ def test_runtime_multi_bundle_check(tmp_path: Path, monkeypatch: pytest.MonkeyPa
|
||||
from lfx.extension.manifest import (
|
||||
BundleRef,
|
||||
ExtensionManifest,
|
||||
LangflowCompat,
|
||||
LfxCompat,
|
||||
ManifestSource,
|
||||
)
|
||||
|
||||
@ -136,7 +136,7 @@ def test_runtime_multi_bundle_check(tmp_path: Path, monkeypatch: pytest.MonkeyPa
|
||||
id="lfx-pilot",
|
||||
version="1.2.3",
|
||||
name="Pilot",
|
||||
lfx=LangflowCompat(bundle_api=[1]),
|
||||
lfx=LfxCompat(compat=["1"]),
|
||||
bundles=[BundleRef(name="alpha", path="alpha"), BundleRef(name="bravo", path="bravo")],
|
||||
)
|
||||
source = ManifestSource.model_construct(manifest=forged, path=tmp_path / "extension.json", kind="extension.json")
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
"""Tests for manifest-first precedence over ``langflow.plugins`` (LE-1015).
|
||||
"""Tests for manifest-first precedence over ``langflow.plugins``.
|
||||
|
||||
Covers the AC item: "package with manifest + legacy component entry-point
|
||||
loads its components ONCE via the manifest, not twice." We model "loaded
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
"""Tests for the loader's public dataclasses (LE-1015).
|
||||
"""Tests for the loader's public dataclasses.
|
||||
|
||||
``LoadedComponent`` and ``LoadResult`` are the values the rest of the
|
||||
Langflow stack consumes (events pipeline, registry, future reload).
|
||||
|
||||
Reference in New Issue
Block a user