Skip to content

Python: Bug: KernelPlugin.from_directory initialises Python classes with no @kernel_function methods #10280

Closed
@Druid-of-Luhn

Description

Describe the bug

The KernelPlugin.from_directory method says (emphasis mine):

A .py file is parsed and a plugin created,
the functions within as [sic] then combined with any other functions found.
The python file needs to contain a class with one or more kernel_function decorated methods.
If this class has a __init__ method, it will be called with the arguments provided in the
class_init_arguments dictionary, the key needs to be the same as the name of the class,
with the value being a dictionary of arguments to pass to the class (using kwargs).

If a .py file is present in the directory, and it has a class with no kernel_function decorated methods, the class will still be initialised when loading the plugin. Since the class is not expected to be initialised, no class_init_arguments are set and it will cause an error.

The use case for this is lazily calling kernel.add_plugin(plugin_name=Path(__file__).parent, parent_directory=Path(__file__).parent.parent) to pick up a prompts.yaml file that is sitting next to the code file. The code file that calls this is also picked up as a plugin, and causes an error since its __init__ parameters were not passed.

I can and will use KernelFunctionFromPrompt.from_yaml instead, since I only want to load the yaml file, but the behaviour still surprised me. I do understand that checking the class definition for decorators before initialising it might be too much/not possible.

To Reproduce

Run the following script:

from pathlib import Path
from semantic_kernel import Kernel

class Example:
    def __init__(self, kernel):
        this_directory = Path(__file__).parent
        kernel.add_plugin(plugin_name=this_directory, parent_directory=this_directory.parent)

if __name__ == "__main__":
    Example(Kernel())

Expected behavior

Since the class has no kernel_function decorated methods, I would expect it to be skipped entirely.

Screenshots

Stack trace:

Traceback (most recent call last):
  File "C:\Users\...\test.py", line 10, in <module>
    Example(Kernel())
  File "C:\Users\...\test.py", line 7, in __init__
    kernel.add_plugin(plugin_name=this_directory, parent_directory=this_directory.parent)
  File "C:\Users\...\.venv\Lib\site-packages\semantic_kernel\functions\kernel_function_extension.py", line 95, in add_plugin
    self.plugins[plugin_name] = KernelPlugin.from_directory(
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\...\.venv\Lib\site-packages\semantic_kernel\functions\kernel_plugin.py", line 325, in from_directory
    cls.from_python_file(
  File "C:\Users\...\.venv\Lib\site-packages\semantic_kernel\functions\kernel_plugin.py", line 401, in from_python_file
    instance = getattr(module, name)(**class_init_arguments.get(name, {}) if class_init_arguments else {})
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: Example.__init__() missing 1 required positional argument: 'kernel'

Platform

  • OS: Windows
  • IDE: gVim + PowerShell
  • Language: Python
  • Source: pip package version 1.19.0

Additional context

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingkernelIssues or pull requests impacting the core kernelpythonPull requests for the Python Semantic Kernel

    Type

    Projects

    • Status

      No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions