Skip to content

Conversation

@ogabrielluiz
Copy link
Contributor

@ogabrielluiz ogabrielluiz commented Nov 30, 2025

What if building a Langflow component was as simple as writing a Python function?

Now it is.

from lfx.base.functions import component

@component
def greet(name: str) -> str:
    """Say hello to someone."""
    return f"Hello, {name}!"

That's it. No classes to inherit. No boilerplate. Just a function that becomes a fully-functional Langflow component with typed inputs, outputs, and graph connectivity.

The Problem

Creating custom components in Langflow requires understanding the Component class hierarchy, defining inputs and outputs lists, and following specific patterns. For simple transformations or utilities, this overhead can feel excessive.

The Solution

FunctionComponent automatically transforms Python functions into Langflow components:

  • Type annotations become inputs - name: str creates a string input
  • Return type becomes output - -> str creates a string output
  • Docstrings become descriptions - Both for the component and individual parameters
  • Default values just work - count: int = 5 creates an input with default
  • Async functions supported - async def works seamlessly

Customize When Needed

from lfx.base.functions import component, InputConfig

@component(display_name="Text Formatter", icon="type")
def format_text(
    text: str,
    uppercase: bool = InputConfig(
        default=False,
        display_name="Convert to Uppercase",
        info="When enabled, converts all text to uppercase"
    )
) -> str:
    """Format text with optional transformations."""
    return text.upper() if uppercase else text

Chain Components Together

@component
def step1(text: str) -> str:
    return text.upper()

@component  
def step2(text: str) -> str:
    return f"[{text}]"

# Connect them
step2.set(text=step1.result)
graph = Graph(step1, step2)

Works in the UI Too

Paste function code directly in Custom Component - it just works. The system automatically detects whether you're writing a class-based component or a function-based one.

Key Features

Feature Description
Zero boilerplate Just decorate a function
Full type inference Inputs/outputs derived from annotations
InputConfig customization Fine-tune any parameter
Persistence Serializes to source code, reconstructs on load
Graph integration .result property for chaining
UI support Works in Custom Component editor
140 tests Comprehensive coverage

Files Changed

  • src/lfx/src/lfx/base/functions/ - New FunctionComponent and @component decorator
  • src/lfx/src/lfx/custom/eval.py - Function detection and wrapper generation
  • src/lfx/src/lfx/custom/validate.py - Code type detection
  • src/lfx/tests/unit/ - 140 tests covering all functionality

Test Plan

Unit tests for FunctionComponent class covering signature parsing, input generation, and execution. Integration tests for graph execution with function components. Tests for @component decorator with various configurations. Tests for eval_custom_component_code function/class detection. Tests for multi-function files with decorator selection. Tests for serialization round-trips. Edge case tests for async, no params, complex types, etc.

Summary by CodeRabbit

Release Notes

  • New Features
    • Added support for creating components from Python functions with the new @component decorator.
    • Automatic input and output generation from function signatures and type hints.
    • Auto-wrapping of plain callables within component graphs as FunctionComponent instances.
    • Docstring parsing for enhanced component metadata and documentation.
    • Function-based component support in custom code evaluation and validation.

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions github-actions bot added the community Pull Request from an external contributor label Nov 30, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 30, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces FunctionComponent, a dynamic wrapper that converts Python functions into Langflow components with automatic UI generation from type hints and docstrings. It includes a component decorator, a from_function factory, InputConfig for customizing input widgets, integration with custom component evaluation and validation systems, and automatic wrapping of plain callables in component workflows.

Changes

Cohort / File(s) Summary
Core FunctionComponent Module
src/lfx/src/lfx/base/functions/__init__.py, src/lfx/src/lfx/base/functions/function_component.py
Introduces FunctionComponent class that introspects Python functions to auto-generate inputs and outputs, InputConfig dataclass for per-parameter UI customization (display_name, info, placeholder, advanced, etc.), component decorator and from_function factory, type-to-widget mappings for String/Int/Float/Bool/Message/Data/Dropdown, docstring parsing for parameter docs, source code capture with fallback stubs, async invocation support, and automatic ID generation.
Custom Component Integration
src/lfx/src/lfx/custom/custom_component/component.py, src/lfx/src/lfx/custom/eval.py, src/lfx/src/lfx/custom/validate.py
Extends eval_custom_component_code to detect and wrap function-based code via _is_function_code and _create_function_component_class; enhances validate_code with _detect_code_type to distinguish function vs. class-based components and populate result metadata; updates component.py with _wrap_function_as_component to auto-wrap plain callables into FunctionComponent during connection processing.
Test Suite
src/lfx/tests/unit/base/functions/test_function_component.py, src/lfx/tests/unit/base/functions/test_function_component_integration.py, src/lfx/tests/unit/custom/test_eval.py, src/lfx/tests/unit/custom/test_validate.py
Comprehensive unit and integration tests covering FunctionComponent creation, docstring parsing, input/output mapping, type coercion, async execution, serialization/deserialization, graph execution, decorator usage, factory functions, edge cases (complex typing, generators, lambdas), function-code detection, component-class wrapping, and validation error handling.

Sequence Diagram

sequenceDiagram
    participant User as Developer
    participant FuncCode as Function Code
    participant Validate as validate_code
    participant Eval as eval_custom_component_code
    participant Wrapper as FunctionComponentWrapper
    participant Component as Component Instance
    participant Graph as Graph
    participant Invoke as invoke_function

    User->>FuncCode: Write `@component` decorated function
    User->>Validate: Submit code for validation
    Validate->>Validate: _detect_code_type via AST
    Validate->>Validate: Mark as function-based
    Validate-->>User: Return validated result
    User->>Eval: eval_custom_component_code
    Eval->>Eval: _is_function_code checks AST
    Eval->>Eval: _create_function_component_class executes code
    Eval->>Wrapper: Create FunctionComponentWrapper subclass
    Wrapper->>Wrapper: Introspect function signature & types
    Wrapper->>Wrapper: Auto-generate inputs from annotations
    Wrapper->>Wrapper: Auto-generate outputs from return type
    Eval-->>Component: Return FunctionComponent instance
    User->>Graph: Add function component to graph
    User->>Graph: Connect inputs and set parameters
    Graph->>Invoke: Call invoke_function during execution
    Invoke->>Invoke: Gather input values, apply defaults
    Invoke->>Invoke: Coerce types (Message→str, dict→Data)
    Invoke->>Invoke: Call wrapped function (sync or async)
    Invoke-->>Graph: Return normalized results
    Graph-->>User: Graph execution complete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Key areas requiring attention:

  • FunctionComponent implementation: Type introspection logic, input/output generation from annotations, docstring parsing, and async/sync invocation handling
  • Decorator overloads: Ensure type hints and usage patterns are correct for both direct decoration and factory usage
  • Integration with eval.py: AST-based function detection, wrapper class creation, and error handling for ambiguous cases (multiple functions, missing decorators)
  • Integration with validate.py: Code type detection logic and result structure changes that flow through downstream consumers
  • Type coercion paths: Verify Message→str, dict→Data, and other type conversions don't introduce unexpected behavior
  • Edge cases in custom_component.py: Plain callable detection and wrapping logic within the connection processing flow

Suggested labels

enhancement, test

Suggested reviewers

  • erichare
  • edwinjosechittilappilly
  • jordanrfrazier

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.64% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Test File Naming And Structure ❓ Inconclusive Test files referenced in PR summary could not be located in repository structure. Verify test files exist in src/lfx/tests/unit/base/functions/ and src/lfx/tests/unit/custom/ directories, confirm they follow test_*.py naming convention, use pytest fixtures, cover positive/negative scenarios, and use descriptive test function names.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main feature: introducing a @component decorator that allows writing Langflow components as plain Python functions, which is the core objective of this PR.
Test Coverage For New Implementations ✅ Passed PR includes comprehensive test coverage with 4 test files covering all new implementations: FunctionComponent, InputConfig, decorators, eval and validate modifications with substantive unit and integration tests.
Test Quality And Coverage ✅ Passed Comprehensive test suite with 77+ tests, 250+ assertions, and ~2,600 lines across four files covering core functionality, async support, serialization, type inference, and extensive edge cases.
Excessive Mock Usage Warning ✅ Passed Direct inspection confirms zero mock framework usage across all test modules; tests use real instances and genuine logic verification.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added the enhancement New feature or request label Nov 30, 2025
@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Nov 30, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Nov 30, 2025

Frontend Unit Test Coverage Report

Coverage Summary

Lines Statements Branches Functions
Coverage: 15%
15.24% (4188/27479) 8.46% (1778/20993) 9.57% (579/6049)

Unit Test Results

Tests Skipped Failures Errors Time
1638 0 💤 0 ❌ 0 🔥 20.775s ⏱️

@codecov
Copy link

codecov bot commented Nov 30, 2025

Codecov Report

❌ Patch coverage is 86.95652% with 51 lines in your changes missing coverage. Please review.
✅ Project coverage is 32.78%. Comparing base (271c7ff) to head (7c1d85c).

Files with missing lines Patch % Lines
...c/lfx/src/lfx/base/functions/function_component.py 85.30% 21 Missing and 20 partials ⚠️
src/lfx/src/lfx/custom/eval.py 91.42% 4 Missing and 2 partials ⚠️
...c/lfx/src/lfx/custom/custom_component/component.py 83.33% 1 Missing and 1 partial ⚠️
src/lfx/src/lfx/custom/validate.py 93.33% 1 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main   #10798      +/-   ##
==========================================
+ Coverage   32.39%   32.78%   +0.39%     
==========================================
  Files        1368     1369       +1     
  Lines       63414    63798     +384     
  Branches     9373     9477     +104     
==========================================
+ Hits        20541    20917     +376     
+ Misses      41840    41801      -39     
- Partials     1033     1080      +47     
Flag Coverage Δ
backend 50.45% <ø> (-0.80%) ⬇️
frontend 14.08% <ø> (ø)
lfx 41.69% <86.95%> (+1.64%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...c/lfx/src/lfx/custom/custom_component/component.py 58.96% <83.33%> (+0.50%) ⬆️
src/lfx/src/lfx/custom/validate.py 60.12% <93.33%> (+19.85%) ⬆️
src/lfx/src/lfx/custom/eval.py 92.00% <91.42%> (-8.00%) ⬇️
...c/lfx/src/lfx/base/functions/function_component.py 85.30% <85.30%> (ø)

... and 37 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions bot removed the enhancement New feature or request label Nov 30, 2025
@github-actions github-actions bot added the enhancement New feature or request label Nov 30, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
src/lfx/src/lfx/custom/validate.py (1)

103-134: Consider tightening the class detection pattern to avoid false positives.

The pattern matching any(pattern in base.id for pattern in ["Component", "LC"]) at line 125 could match unintended classes like MyLCHelper or ComponentFactory that aren't actual component base classes.

Consider using more specific patterns:

-                if isinstance(base, ast.Name) and any(pattern in base.id for pattern in ["Component", "LC"]):
+                # Match exact Component or classes ending with Component/starting with LC
+                if isinstance(base, ast.Name) and (
+                    base.id == "Component"
+                    or base.id.endswith("Component")
+                    or base.id.startswith("LC")
+                ):

Note: The same pattern is used in extract_class_name (line 578), so consider updating both for consistency.

src/lfx/src/lfx/custom/eval.py (1)

115-118: Potential silent failure if function execution raises but doesn't propagate.

If the function definition exists in the AST but exec() fails to populate namespace (e.g., due to an import error within the function body that doesn't raise immediately), func could be None. The current error message would be misleading in that case.

Consider enhancing the error message to indicate potential execution issues:

     func = namespace.get(func_name)
     if func is None:
-        msg = f"Function '{func_name}' not found in namespace after execution"
+        msg = (
+            f"Function '{func_name}' not found in namespace after execution. "
+            f"This may indicate an import or syntax error within the function."
+        )
         raise ValueError(msg)
src/lfx/tests/unit/custom/test_eval.py (1)

340-402: Consider moving ast import to module level.

The import ast statement is repeated inside each test method. Since ast is a standard library module used by multiple tests, moving it to the module-level imports would be cleaner.

+import ast
+
 import pytest
 from lfx.base.functions import FunctionComponent
 from lfx.custom.eval import (

Then remove the import ast lines from within each test method.

src/lfx/src/lfx/base/functions/function_component.py (1)

518-540: Decorator stripping may fail with multi-line decorator arguments.

The _strip_decorators_and_dedent method handles single-line decorators but may not correctly handle multi-line decorator arguments:

@component(
    display_name="My Component",
    description="Long description"
)
def my_func(): ...

In this case, lines between @component( and ) would be incorrectly included in func_lines.

Consider using AST-based decorator stripping instead, which would be more robust:

def _strip_decorators_and_dedent(self, source: str) -> str:
    """Strip decorator lines and dedent the function source."""
    import ast
    import textwrap

    try:
        tree = ast.parse(textwrap.dedent(source))
        for node in ast.walk(tree):
            if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
                node.decorator_list = []
        return ast.unparse(tree)
    except SyntaxError:
        # Fallback to line-based stripping
        # ... existing logic ...

Note: ast.unparse() requires Python 3.9+. Verify compatibility.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 271c7ff and 2a5be66.

📒 Files selected for processing (9)
  • src/lfx/src/lfx/base/functions/__init__.py (1 hunks)
  • src/lfx/src/lfx/base/functions/function_component.py (1 hunks)
  • src/lfx/src/lfx/custom/custom_component/component.py (3 hunks)
  • src/lfx/src/lfx/custom/eval.py (2 hunks)
  • src/lfx/src/lfx/custom/validate.py (2 hunks)
  • src/lfx/tests/unit/base/functions/test_function_component.py (1 hunks)
  • src/lfx/tests/unit/base/functions/test_function_component_integration.py (1 hunks)
  • src/lfx/tests/unit/custom/test_eval.py (1 hunks)
  • src/lfx/tests/unit/custom/test_validate.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/{test_*.py,*.test.ts,*.test.tsx}

📄 CodeRabbit inference engine (Custom checks)

Check that test files follow the project's naming conventions (test_*.py for backend, *.test.ts for frontend)

Files:

  • src/lfx/tests/unit/base/functions/test_function_component.py
  • src/lfx/tests/unit/custom/test_validate.py
  • src/lfx/tests/unit/base/functions/test_function_component_integration.py
  • src/lfx/tests/unit/custom/test_eval.py
**/test_*.py

📄 CodeRabbit inference engine (Custom checks)

**/test_*.py: Backend tests should follow pytest structure with proper test_*.py naming
For async functions, ensure proper async testing patterns are used with pytest for backend

Files:

  • src/lfx/tests/unit/base/functions/test_function_component.py
  • src/lfx/tests/unit/custom/test_validate.py
  • src/lfx/tests/unit/base/functions/test_function_component_integration.py
  • src/lfx/tests/unit/custom/test_eval.py
🧠 Learnings (14)
📚 Learning: 2025-11-24T19:46:09.104Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/backend_development.mdc:0-0
Timestamp: 2025-11-24T19:46:09.104Z
Learning: Applies to tests/unit/components/**/*.py : Create unit tests in `src/backend/tests/unit/components/` mirroring the component directory structure, using `ComponentTestBaseWithClient` or `ComponentTestBaseWithoutClient` base classes

Applied to files:

  • src/lfx/tests/unit/base/functions/test_function_component.py
  • src/lfx/tests/unit/base/functions/test_function_component_integration.py
📚 Learning: 2025-11-24T19:47:28.997Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.997Z
Learning: Applies to src/backend/tests/**/*.py : Test component build config updates by calling `to_frontend_node()` to get the node template, then calling `update_build_config()` to apply configuration changes

Applied to files:

  • src/lfx/tests/unit/base/functions/test_function_component.py
  • src/lfx/tests/unit/base/functions/test_function_component_integration.py
  • src/lfx/tests/unit/custom/test_eval.py
📚 Learning: 2025-11-24T19:47:28.997Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.997Z
Learning: Applies to src/backend/tests/**/*.py : Inherit from the correct `ComponentTestBase` family class located in `src/backend/tests/base.py` based on API access needs: `ComponentTestBase` (no API), `ComponentTestBaseWithClient` (needs API), or `ComponentTestBaseWithoutClient` (pure logic). Provide three required fixtures: `component_class`, `default_kwargs`, and `file_names_mapping`

Applied to files:

  • src/lfx/tests/unit/base/functions/test_function_component.py
  • src/lfx/tests/unit/base/functions/test_function_component_integration.py
  • src/lfx/tests/unit/custom/test_eval.py
📚 Learning: 2025-11-24T19:47:28.997Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.997Z
Learning: Applies to src/backend/tests/**/*.py : Test component versioning and backward compatibility using `file_names_mapping` fixture with `VersionComponentMapping` objects mapping component files across Langflow versions

Applied to files:

  • src/lfx/tests/unit/base/functions/test_function_component.py
  • src/lfx/tests/unit/base/functions/test_function_component_integration.py
📚 Learning: 2025-11-24T19:47:28.997Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.997Z
Learning: Applies to src/backend/tests/**/*.py : Test both sync and async code paths, mock external dependencies appropriately, test error handling and edge cases, validate input/output behavior, and test component initialization and configuration

Applied to files:

  • src/lfx/tests/unit/base/functions/test_function_component.py
  • src/lfx/tests/unit/custom/test_validate.py
  • src/lfx/tests/unit/base/functions/test_function_component_integration.py
  • src/lfx/tests/unit/custom/test_eval.py
📚 Learning: 2025-11-24T19:47:28.997Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.997Z
Learning: When adding a new component test, inherit from the correct `ComponentTestBase` class and provide the three required fixtures (`component_class`, `default_kwargs`, `file_names_mapping`) to greatly reduce boilerplate and enforce version compatibility

Applied to files:

  • src/lfx/tests/unit/base/functions/test_function_component.py
📚 Learning: 2025-08-05T22:51:27.961Z
Learnt from: edwinjosechittilappilly
Repo: langflow-ai/langflow PR: 0
File: :0-0
Timestamp: 2025-08-05T22:51:27.961Z
Learning: The TestComposioComponentAuth test in src/backend/tests/unit/components/bundles/composio/test_base_composio.py demonstrates proper integration testing patterns for external API components, including real API calls with mocking for OAuth completion, comprehensive resource cleanup, and proper environment variable handling with pytest.skip() fallbacks.

Applied to files:

  • src/lfx/tests/unit/base/functions/test_function_component.py
  • src/lfx/tests/unit/base/functions/test_function_component_integration.py
  • src/lfx/tests/unit/custom/test_eval.py
📚 Learning: 2025-11-24T19:47:28.997Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.997Z
Learning: Applies to src/backend/tests/**/*.py : Create comprehensive unit tests for all new backend components. If unit tests are incomplete, create a corresponding Markdown file documenting manual testing steps and expected outcomes

Applied to files:

  • src/lfx/tests/unit/base/functions/test_function_component.py
  • src/lfx/tests/unit/custom/test_validate.py
  • src/lfx/tests/unit/base/functions/test_function_component_integration.py
  • src/lfx/tests/unit/custom/test_eval.py
📚 Learning: 2025-11-24T19:47:40.400Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2025-11-24T19:47:40.400Z
Learning: Applies to **/*.{test.ts,test.tsx,spec.ts,spec.tsx,test_*.py} : Tests should cover the main functionality being implemented

Applied to files:

  • src/lfx/tests/unit/base/functions/test_function_component.py
📚 Learning: 2025-11-24T19:46:09.104Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/backend_development.mdc:0-0
Timestamp: 2025-11-24T19:46:09.104Z
Learning: Applies to src/backend/base/langflow/components/**/__init__.py : Update `__init__.py` with alphabetically sorted imports when adding new components

Applied to files:

  • src/lfx/src/lfx/base/functions/__init__.py
  • src/lfx/src/lfx/base/functions/function_component.py
📚 Learning: 2025-11-24T19:46:09.104Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/backend_development.mdc:0-0
Timestamp: 2025-11-24T19:46:09.104Z
Learning: Applies to src/backend/base/langflow/components/**/*.py : Add new components to the appropriate subdirectory under `src/backend/base/langflow/components/` (agents/, data/, embeddings/, input_output/, models/, processing/, prompts/, tools/, or vectorstores/)

Applied to files:

  • src/lfx/src/lfx/base/functions/function_component.py
📚 Learning: 2025-11-24T19:47:28.997Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.997Z
Learning: Applies to src/backend/tests/**/*.py : Be aware of ContextVar propagation in async tests; test both direct event loop execution and `asyncio.to_thread` scenarios; ensure proper context isolation between test cases

Applied to files:

  • src/lfx/tests/unit/base/functions/test_function_component_integration.py
📚 Learning: 2025-11-24T19:47:28.997Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.997Z
Learning: Applies to src/backend/tests/**/*.py : Use `pytest.mark.asyncio` decorator for async component tests and ensure async methods are properly awaited

Applied to files:

  • src/lfx/tests/unit/base/functions/test_function_component_integration.py
  • src/lfx/tests/unit/custom/test_eval.py
📚 Learning: 2025-11-24T19:47:28.997Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.997Z
Learning: Applies to src/backend/tests/**/*.py : Use same filename as component with appropriate test prefix/suffix (e.g., `my_component.py` → `test_my_component.py`)

Applied to files:

  • src/lfx/tests/unit/base/functions/test_function_component_integration.py
🧬 Code graph analysis (5)
src/lfx/tests/unit/base/functions/test_function_component.py (7)
src/lfx/src/lfx/base/functions/function_component.py (3)
  • FunctionComponent (96-540)
  • InputConfig (71-93)
  • invoke_function (446-494)
src/lfx/src/lfx/schema/data.py (1)
  • Data (26-288)
src/lfx/src/lfx/schema/message.py (1)
  • Message (34-299)
src/lfx/src/lfx/base/tools/flow_tool.py (1)
  • args (32-34)
src/lfx/src/lfx/template/field/base.py (1)
  • Output (181-260)
src/lfx/src/lfx/components/input_output/chat_output.py (1)
  • ChatOutput (22-184)
src/lfx/src/lfx/custom/custom_component/component.py (1)
  • to_frontend_node (1021-1073)
src/lfx/tests/unit/custom/test_validate.py (1)
src/lfx/src/lfx/custom/validate.py (2)
  • _detect_code_type (103-134)
  • validate_code (31-100)
src/lfx/src/lfx/custom/custom_component/component.py (1)
src/lfx/src/lfx/base/functions/function_component.py (1)
  • FunctionComponent (96-540)
src/lfx/src/lfx/base/functions/__init__.py (1)
src/lfx/src/lfx/base/functions/function_component.py (6)
  • FunctionComponent (96-540)
  • InputConfig (71-93)
  • component (565-565)
  • component (569-574)
  • component (577-629)
  • from_function (543-561)
src/lfx/tests/unit/custom/test_eval.py (2)
src/lfx/src/lfx/base/functions/function_component.py (2)
  • FunctionComponent (96-540)
  • invoke_function (446-494)
src/lfx/src/lfx/custom/eval.py (4)
  • _create_function_component_class (58-127)
  • _has_component_decorator (130-146)
  • _is_function_code (25-55)
  • eval_custom_component_code (11-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Update Starter Projects
  • GitHub Check: Update Component Index
🔇 Additional comments (33)
src/lfx/tests/unit/custom/test_validate.py (1)

1-210: LGTM! Comprehensive test coverage for code validation.

The test file follows pytest conventions with proper test_*.py naming. It provides thorough coverage of:

  • Function and async function detection
  • Class-based component detection (including LC-prefixed classes)
  • Priority logic (class over function when both exist)
  • Edge cases (empty code, syntax errors, import errors)
  • The internal _detect_code_type helper

Tests are synchronous, which is appropriate since the validate_code function is synchronous. Based on learnings, this aligns with the guideline to "test error handling and edge cases, validate input/output behavior."

src/lfx/src/lfx/custom/custom_component/component.py (4)

50-50: LGTM! Correct use of TYPE_CHECKING for conditional import.

The FunctionComponent import under TYPE_CHECKING avoids circular dependency at runtime while enabling proper type hints.


684-693: LGTM! Correct identification of plain callables.

The method correctly filters to only plain Python functions and coroutine functions, excluding:

  • Non-callables
  • Component bound methods
  • Built-in functions, classes, and other callable types

Lambdas will pass inspect.isfunction(), which is appropriate behavior.


695-699: LGTM! Clean factory method with local import.

The local import of FunctionComponent inside the method correctly avoids circular import issues at module load time.


792-796: LGTM! Auto-wrapping of plain functions enables cleaner API.

The auto-wrapping logic correctly:

  1. Detects plain callables (not already Component methods)
  2. Wraps them in FunctionComponent
  3. Connects via fc.result which returns the bound invoke_function method

This enables users to pass plain functions directly to Component.set() without manually wrapping them.

src/lfx/src/lfx/base/functions/__init__.py (1)

1-15: LGTM! Clean public API surface.

The module correctly exposes the public API with:

  • Explicit imports from the implementation module
  • __all__ declaration for clear public interface
  • Alphabetically sorted exports (as per coding guidelines)

This provides a clean entry point for users: from lfx.base.functions import FunctionComponent, component

src/lfx/tests/unit/base/functions/test_function_component.py (8)

1-12: LGTM! Well-organized test imports and structure.

The test file follows pytest conventions with proper imports from the module under test. The use of warnings module for testing warning behavior is appropriate.


14-136: LGTM! Thorough signature introspection tests.

Excellent coverage of FunctionComponent creation including:

  • Single/multiple parameters
  • Default values
  • Type hints (list, Literal, Optional)
  • Warning for untyped parameters
  • Skipping special parameters (self, cls, *args, **kwargs)

The use of warnings.catch_warnings() for testing warning behavior is the correct pattern.


139-173: LGTM! Docstring parsing tests are appropriate.

Tests cover Google-style docstring parsing and the no-docstring case. The assertion on fc._description correctly accounts for the underscore-prefixed attribute used by the Component base class.


176-342: LGTM! Comprehensive metadata and decorator tests.

Good coverage of:

  • Output type mapping (str→Message, dict→Data, no return type)
  • Display name derivation from function name
  • ID generation (auto-generated and custom)
  • Factory function from_function()
  • Decorator usage with and without parameters
  • InputConfig customization via Annotated types

345-474: LGTM! Execution and serialization tests are well-structured.

Proper use of pytest.mark.asyncio for async tests. Good coverage of:

  • Type coercion (Message→str input, dict→Data output)
  • Sync and async function execution
  • Exception propagation
  • The result property for graph chaining
  • Source code capture for persistence

Based on learnings, this follows the guideline to "test both sync and async code paths."


477-740: LGTM! Auto-wrapping and connection tests are thorough.

Good coverage of:

  • Auto-wrapping plain functions via Component.set()
  • Connecting FunctionComponents to ChatInput/ChatOutput
  • Edge data verification (sourceHandle, targetHandle, types)
  • Edge cases: None return, Union types, lambdas, generators, complex nested types

The tests correctly verify the new auto-wrapping behavior introduced in component.py.


742-1014: LGTM! Serialization and type validation tests are comprehensive.

Excellent coverage of:

  • Frontend node serialization
  • Edge serialization correctness
  • Type mismatch detection (dict→str, int→str, list→single, Data→Message)
  • Valid connection verification (str→str via Message type)

The tests verify that Graph correctly rejects invalid type connections with ValueError.


1017-1144: LGTM! Name attribute and simple docstring format tests.

Good coverage of:

  • name attribute consistency with _display_name
  • Custom display name propagation to name
  • Simple docstring format ("param: description") without Args: section
  • Priority of Args: section over simple format when both exist

The simple docstring format tests ensure backward compatibility with various documentation styles.

src/lfx/tests/unit/base/functions/test_function_component_integration.py (7)

1-19: LGTM! Clear test file organization and imports.

The docstring clearly documents the test scope (graph execution, serialization, deserialization). Imports are minimal and appropriate.


21-159: LGTM! Graph execution tests are comprehensive.

Good coverage of:

  • Simple and decorated functions in graphs
  • Chained FunctionComponents
  • Multiple parameters with defaults
  • Async functions

All tests correctly use pytest.mark.asyncio and properly await async operations.


162-240: LGTM! Serialization tests verify structure correctness.

Tests properly verify:

  • graph.dump() includes nodes and edges
  • graph.dumps() produces valid parseable JSON
  • Edge references point to valid node IDs

243-386: LGTM! Deserialization and round-trip tests are thorough.

Good coverage of:

  • Simple function round-trip
  • Explicit ID handling (with note about 'chat' prefix requirement)
  • Chained function round-trip
  • Structural verification (vertex/edge counts)

The comment about ChatInput IDs needing 'chat' prefix (lines 289-291, 297-298) is helpful documentation for test maintainability.


389-455: LGTM! Type handling tests cover common scenarios.

Tests verify correct handling of:

  • Integer parameters
  • Boolean parameters
  • Dict return values wrapped as Data

457-736: LGTM! Function-only pipeline tests are valuable.

Excellent coverage of pipelines without ChatInput/ChatOutput:

  • Various chain lengths (2, 3, 5 components)
  • Mixed sync/async components
  • Default parameter handling
  • Round-trip serialization
  • Graph structure verification (vertex IDs, edge pairs)

This demonstrates FunctionComponent can be used in pure function pipelines, not just chat-based workflows.


739-832: LGTM! Edge case tests ensure robustness.

Good coverage of:

  • Empty string handling
  • Special characters (newlines, tabs, quotes, HTML entities)
  • Unicode (emoji, Chinese, Russian characters)
  • Multiple serialization round-trips (3x) without degradation

These tests ensure the implementation handles real-world input gracefully.

src/lfx/src/lfx/custom/validate.py (2)

31-53: Well-structured result dictionary for code validation.

The refactored validate_code function now returns a comprehensive result dictionary that supports both function-based and class-based component detection. The structure is clear and extensible.


89-89: The isinstance() union type syntax is compatible with the project's minimum Python version.

The project specifies requires-python = ">=3.10,<3.14" in pyproject.toml. The ast.FunctionDef | ast.AsyncFunctionDef syntax in isinstance() is supported in Python 3.10+, so no compatibility issue exists.

src/lfx/src/lfx/custom/eval.py (2)

71-73: Security consideration: exec() on user-provided code.

The exec() call executes arbitrary code from the input string. While this appears intentional for custom component evaluation, ensure this code path is only accessible to authenticated users with appropriate permissions, and never exposed to untrusted input.

The # noqa: S102 comment indicates awareness of this security implication. Please confirm that:

  1. This function is only called with code from trusted sources (e.g., authenticated user submissions)
  2. There are appropriate access controls at the API layer

130-145: Good coverage of decorator detection patterns.

The _has_component_decorator function handles various decorator forms well:

  • @component
  • @component(...)
  • @lfx.base.functions.component
  • @lfx.base.functions.component(...)
src/lfx/tests/unit/custom/test_eval.py (3)

1-13: Comprehensive test coverage for function-based component evaluation.

The test file follows pytest structure with proper test_*.py naming. Good organization with test classes grouped by functionality. As per coding guidelines, backend tests follow pytest structure correctly.


266-290: Proper async testing pattern used.

The async tests correctly use @pytest.mark.asyncio decorator as required for async component tests. Based on learnings, async functions should use proper async testing patterns with pytest.


293-334: Good round-trip serialization tests.

These tests verify that FunctionComponent instances can be serialized via set_class_code() and recreated through eval_custom_component_code. This is essential for persistence and UI integration.

src/lfx/src/lfx/base/functions/function_component.py (5)

49-67: Well-defined type mappings for input/output conversion.

The TYPE_TO_INPUT_CLASS and TYPE_TO_OUTPUT_TYPE dictionaries provide clear mappings between Python types and Langflow UI components. This is a clean approach for type inference.


70-93: InputConfig dataclass provides flexible input customization.

Good use of dataclass with sensible defaults. The required: bool | None = None allowing inference from defaults is a nice touch.


564-628: Clean decorator implementation with proper overloads.

The @component decorator is well-implemented with:

  • Proper @overload signatures for type checking
  • Support for both @component and @component(...) syntax
  • Source code capture at decoration time
  • Metadata preservation via functools.update_wrapper

446-494: Robust invoke_function with proper async handling and type coercion.

The method correctly:

  • Handles async functions via inspect.iscoroutine
  • Coerces Messagestr when expected
  • Coerces dictData in results
  • Falls back to signature defaults when values are empty

460-460: Potential KeyError when accessing self._inputs[param_name].

If param_name is a valid signature parameter but not present in self._inputs (e.g., due to a filtering condition during input building), this will raise a KeyError.

Consider using .get() with a fallback:

-            value = self._inputs[param_name].value if param_name in self._inputs else getattr(self, param_name, None)
+            input_obj = self._inputs.get(param_name)
+            value = input_obj.value if input_obj else getattr(self, param_name, None)

This is safer and more idiomatic Python.

Likely an incorrect or invalid review comment.

Comment on lines +326 to +329
if origin in (list, List if "List" in dir() else list): # noqa: F821
is_list = True
args = get_args(param_type)
param_type = args[0] if args else str
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Dead code: List check will always evaluate to list.

The check List if "List" in dir() else list will always result in list because List is not imported in this module's scope. The same issue exists on line 436.

-        if origin in (list, List if "List" in dir() else list):  # noqa: F821
+        if origin is list:
             is_list = True

And similarly for line 436:

-        if origin in (list, List if "List" in dir() else list):  # noqa: F821
+        if origin is list:
             return ["list"]

Note: If you need to support typing.List annotations from older code, import it and check both:

from typing import List
# ...
if origin in (list, List):
🤖 Prompt for AI Agents
In src/lfx/src/lfx/base/functions/function_component.py around lines 326-329
(and likewise update line 436), the conditional uses `List if "List" in dir()
else list` which always evaluates to `list`; import `List` from `typing` at the
top of the file and replace the conditional with a clear check that tests both
built-in list and typing.List (e.g., `if origin in (list, List):`) at both
locations so annotations using `typing.List` are correctly recognized.

- Add comprehensive module-level docstring with quick start guide
- Document InputConfig attributes and both usage patterns (default value + Annotated)
- Add examples for chaining, async functions, supported types
- Implement InputConfig.default for cleaner default value syntax
- Add password support using SecretStrInput
- Add tests for InputConfig as default value syntax
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a powerful new feature that allows developers to create Langflow components from plain Python functions using a @component decorator, eliminating the need for class-based boilerplate. The implementation automatically converts function signatures to component inputs/outputs, supports async functions, and maintains full serialization/deserialization capabilities.

Key changes:

  • New FunctionComponent class that wraps Python functions as Langflow components
  • @component decorator for simple function-to-component transformation
  • Auto-detection of function vs class code in custom component evaluation
  • Support for InputConfig to customize input fields with type-safe annotations

Reviewed changes

Copilot reviewed 13 out of 15 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
src/lfx/src/lfx/base/functions/function_component.py Core implementation of FunctionComponent class with signature parsing, input/output generation, and execution logic
src/lfx/src/lfx/base/functions/__init__.py Module exports for FunctionComponent, InputConfig, component decorator, and from_function factory
src/lfx/src/lfx/custom/eval.py Extended eval logic to detect and handle function-based components alongside class-based components
src/lfx/src/lfx/custom/validate.py Added code type detection (function vs class) to validation with new _detect_code_type helper
src/lfx/src/lfx/custom/custom_component/component.py Added auto-wrapping of plain callables as FunctionComponents when using Component.set()
src/lfx/tests/unit/base/functions/test_function_component.py Comprehensive unit tests covering FunctionComponent creation, configuration, and edge cases (1227 lines)
src/lfx/tests/unit/base/functions/test_function_component_integration.py Integration tests for graph execution, serialization, and round-trip scenarios (832 lines)
src/lfx/tests/unit/custom/test_eval.py Tests for custom component code evaluation with function/class detection (402 lines)
src/lfx/tests/unit/custom/test_validate.py Tests for code validation and type detection logic (210 lines)
Comments suppressed due to low confidence (1)

src/lfx/src/lfx/custom/custom_component/component.py:181

        self.set_class_code()

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.

Comment on lines +650 to +653
# Get value from inputs first, fall back to attributes
# Note: We must use _inputs directly because getattr may return
# component attributes (like 'name' for display_name) instead of input values
value = self._inputs[param_name].value if param_name in self._inputs else getattr(self, param_name, None)
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Potential performance issue: self._inputs[param_name].value is accessed for every parameter, but then falls back to getattr(self, param_name, None) which could be slow if there are many parameters. Consider caching the input values or restructuring to avoid repeated lookups.

The comment mentions avoiding getattr for components attributes, but then uses it anyway as a fallback.

Copilot uses AI. Check for mistakes.
origin = get_origin(param_type)

# Check for list type
if origin in (list, List if "List" in dir() else list): # noqa: F821
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition if origin in (list, List if "List" in dir() else list) has a problematic expression. Using "List" in dir() checks the local namespace which is unreliable. This should import List from typing at the top of the file and use a proper check.

Consider: if origin in (list, typing.List) after adding import typing or from typing import List

Suggested change
if origin in (list, List if "List" in dir() else list): # noqa: F821
if origin in (list, List):

Copilot uses AI. Check for mistakes.
types.extend(self._get_output_types(arg))
return types

if origin in (list, List if "List" in dir() else list): # noqa: F821
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition if origin in (list, List if "List" in dir() else list) has a problematic expression. Using "List" in dir() checks the local namespace which is unreliable. This should import List from typing at the top of the file and use a proper check.

Consider: if origin in (list, typing.List) after adding import typing or from typing import List

Copilot uses AI. Check for mistakes.
value = self._inputs[param_name].value if param_name in self._inputs else getattr(self, param_name, None)

# If value is None or empty string, use the default from function signature
if (value is None or value == "") and param.default is not inspect.Parameter.empty:
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition checks (value is None or value == "") which will treat empty strings as None for all parameter types. This could be problematic for optional string parameters where an empty string is a valid value distinct from None.

Consider checking the parameter type before treating empty string as None, or document this behavior explicitly.

Copilot uses AI. Check for mistakes.
if isinstance(raw_default, InputConfig):
# InputConfig used as default value: `param: str = InputConfig(default="value")`
input_config_from_default = raw_default
default = raw_default.default
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing input validation for the default parameter in InputConfig. When default is set via InputConfig, if it doesn't match the parameter type, it could cause runtime errors during execution.

Consider adding type validation to ensure the default value is compatible with the parameter type.

Suggested change
default = raw_default.default
default = raw_default.default
# Type validation for default value
if default is not None:
origin = get_origin(param_type)
args = get_args(param_type)
expected_type = param_type
# Handle Optional[X] or Union[X, None]
if origin is Union and type(None) in args:
# Remove NoneType from args
non_none_args = tuple(a for a in args if a is not type(None))
if len(non_none_args) == 1:
expected_type = non_none_args[0]
else:
expected_type = Union[non_none_args]
# For generic types, get the origin
elif origin is not None:
expected_type = origin
# Now check type
if not isinstance(default, expected_type):
try:
# Try to coerce
default = expected_type(default)
except Exception:
warnings.warn(
f"Default value '{default}' for parameter '{param_name}' in function '{self._func_name}' "
f"does not match expected type '{expected_type.__name__}'.",
stacklevel=3,
)

Copilot uses AI. Check for mistakes.

for node in tree.body:
# Check for function definitions
if isinstance(node, ast.FunctionDef | ast.AsyncFunctionDef):
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition isinstance(node, ast.FunctionDef | ast.AsyncFunctionDef) uses a union type with isinstance(), but the second argument to isinstance() should be a tuple, not a union. This will raise a TypeError at runtime.

Change to: isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))

Suggested change
if isinstance(node, ast.FunctionDef | ast.AsyncFunctionDef):
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):

Copilot uses AI. Check for mistakes.

# Execute code to get the functions
namespace: dict = {}
exec(dedented_code, namespace) # noqa: S102
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code uses exec() on user-provided code string without any restrictions. This is a critical security vulnerability that could allow arbitrary code execution. Consider using a more restricted execution environment or at minimum add explicit warnings about the security implications.

If this is intended for trusted code only, document this requirement clearly.

Copilot uses AI. Check for mistakes.
Comment on lines +717 to +731
in_decorator = False

for line in lines:
stripped = line.strip()
# Skip decorator lines (including multi-line decorators)
if stripped.startswith("@"):
in_decorator = True
continue
# Once we hit def/async def, we're past decorators
if in_decorator and stripped.startswith(("def ", "async def ")):
in_decorator = False
# Include non-decorator lines
if not in_decorator or stripped.startswith(("def ", "async def ")):
func_lines.append(line)
in_decorator = False
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _strip_decorators_and_dedent method has a logic issue. Once in_decorator is set to False when encountering def/async def, it should not be reset again. The current logic may skip lines incorrectly.

The condition if not in_decorator or stripped.startswith(("def ", "async def ")) should be: if not in_decorator. The second part of the OR is redundant since in_decorator is already False at that point.

Suggested change
in_decorator = False
for line in lines:
stripped = line.strip()
# Skip decorator lines (including multi-line decorators)
if stripped.startswith("@"):
in_decorator = True
continue
# Once we hit def/async def, we're past decorators
if in_decorator and stripped.startswith(("def ", "async def ")):
in_decorator = False
# Include non-decorator lines
if not in_decorator or stripped.startswith(("def ", "async def ")):
func_lines.append(line)
in_decorator = False
in_decorator = True
for line in lines:
stripped = line.strip()
# Skip decorator lines (including multi-line decorators)
if in_decorator:
if stripped.startswith("@"):
continue
if stripped.startswith(("def ", "async def ")):
in_decorator = False
func_lines.append(line)
# else: still in decorator section, skip line
else:
func_lines.append(line)

Copilot uses AI. Check for mistakes.
# Check for Component class definitions
elif isinstance(node, ast.ClassDef):
for base in node.bases:
if isinstance(base, ast.Name) and any(pattern in base.id for pattern in ["Component", "LC"]):
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pattern check any(pattern in base.id for pattern in ["Component", "LC"]) is overly broad and could match unintended classes. For example, a class named MyLCDisplay or ComponentHelper that doesn't inherit from the Langflow Component class would incorrectly match.

Consider using more specific checks like base.id.endswith("Component") or maintain a list of known base class names.

Suggested change
if isinstance(base, ast.Name) and any(pattern in base.id for pattern in ["Component", "LC"]):
if isinstance(base, ast.Name) and (base.id.endswith("Component") or base.id == "LC"):

Copilot uses AI. Check for mistakes.
has_function = True
elif isinstance(node, ast.ClassDef):
for base in node.bases:
if isinstance(base, ast.Name) and any(pattern in base.id for pattern in ["Component", "LC"]):
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pattern check any(pattern in base.id for pattern in ["Component", "LC"]) is overly broad and could match unintended classes. For example, a class named MyLCDisplay or ComponentHelper that doesn't inherit from the Langflow Component class would incorrectly match.

Consider using more specific checks like base.id.endswith("Component") or maintain a list of known base class names.

Suggested change
if isinstance(base, ast.Name) and any(pattern in base.id for pattern in ["Component", "LC"]):
if isinstance(base, ast.Name) and (base.id == "Component" or base.id == "LC" or base.id.endswith("Component")):

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community Pull Request from an external contributor enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant