Python: Bug: KernelPlugin.from_directory initialises Python classes with no @kernel_function methods #10280
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
Labels
Type
Projects
Status
No status