
    Ni+                         U d Z ddlZddlZddlZddlmZ ddlmZm	Z	m
Z
 ddlmZ e G d d                      Ze G d d	                      Z G d
 d          Zdae
e         ed<   defdZddedefdZdS )z
Plugin Discovery and Loading System

Automatically discovers and loads plugins from the plugins/ directory.
Each plugin must be in a subdirectory with a plugin.py file containing a *Plugin class.
    N)Path)DictAnyOptional)	dataclassc                       e Zd ZU dZeed<   eed<   eed<   dZeed<   dZe	ed<   d	Z
eed
<   dZee         ed<   d	Zeed<   dZeed<   dZeed<   dZeed<   dZeed<   dZeed<   dZeed<   d ZdS )PluginParameterzODefines a parameter for the plugin, used for both validation and UI generation.nametypelabelFrequiredNdefault placeholderoptions	help_textminmax
min_length
max_lengthpatternpattern_descriptionc                 @    | j         | j        dk    rg | _         d S d S d S )Nselect)r   r   selfs    6/home/byschii/byschiidev/penelope/lib/plugin_loader.py__post_init__zPluginParameter.__post_init__%   s/    <DI$9$9DLLL  $9$9    )__name__
__module____qualname____doc__str__annotations__r   boolr   r   r   r   listr   r   floatr   r   intr   r   r   r    r   r   r	   r	      s         YY
III
IIIJJJHdGSKGT#YIs CCJJGS####    r   r	   c                   |    e Zd ZU dZeed<   eed<   dZee         ed<   dZee	         ed<   dZ
ee         ed<   d ZdS )	PluginMetadataz?Plugin metadata describing the plugin's interface and behavior.r
   descriptionNmethods
parametersbg_colorc                 D    | j         dg| _         | j        	g | _        d S d S )NPOST)r.   r/   r   s    r   r   zPluginMetadata.__post_init__3   s.    <"8DL?" DOOO #"r   )r    r!   r"   r#   r$   r%   r.   r'   r/   r	   r0   r   r*   r   r   r,   r,   *   s}         II
IIIGT#Y(,J_%,,,Hd3i! ! ! ! !r   r,   c                       e Zd ZdZddefdZddZded	eddfd
Zde	dede
e         fdZdede
e         fdZdeeef         fdZdeeef         fdZdS )PluginRegistryz
    Central registry for all discovered plugins.
    
    Scans the plugins/ directory, loads valid plugin classes, and maintains
    a mapping of plugin names to plugin instances.
    pluginsplugins_dirc                 J    t          |          | _        i | _        i | _        dS )z
        Initialize the plugin registry.
        
        Args:
            plugins_dir: Path to the directory containing plugin subdirectories
        N)r   r6   r5   _load_errors)r   r6   s     r   __init__zPluginRegistry.__init__B   s)      ,,'),.r   returnNc                    | j                                         sLt          d| j                     t          d| j                     | j                             dd           dS | j                                         st          d| j                     dS d | j                                         D             }|st          d| j                     dS t          d	| j          d
           |D ]Y}|dz  }|                                s(d| j        |j        <   t          d|j         d           C|                     ||           Zt          dd            t          dt          | j
                   d           | j        r%t          dt          | j                   d           t          d d           dS )aU  
        Scan plugins directory and load all valid plugins.
        
        Expected structure:
            plugins/
                wikipedia_search/
                    plugin.py  # Contains *Plugin class
                form_filler/
                    plugin.py
        
        Logs success/failure for each plugin discovered.
        u%   ⚠️  Plugins directory not found: z   Creating directory: T)parentsexist_okNu0   ❌ Plugins path exists but is not a directory: c                 n    g | ]2}|                                 |j                            d           0|3S )_)is_dirr
   
startswith).0ps     r   
<listcomp>z4PluginRegistry.discover_and_load.<locals>.<listcomp>e   s=    mmm188::mVWV\VgVghkVlVlm!mmmr   u'   ℹ️  No plugin directories found in u   
🔍 Scanning for plugins in z...z	plugin.pyzMissing plugin.py file   ⊘  z: Missing plugin.py file
z<============================================================u   ✅ Successfully loaded z
 plugin(s)u   ❌ Failed to load )r6   existsprintmkdirr@   iterdirr8   r
   _load_plugin_from_filelenr5   )r   plugin_foldersplugin_folderplugin_files       r   discover_and_loadz PluginRegistry.discover_and_loadM   s    &&(( 	L$:JLLMMM>D,<>>???""4$"???F&&(( 	WTEUWWXXXF nmT%5%=%=%?%?mmm 	ND<LNNOOOFE0@EEEFFF+ 		D 		DM'+5K%%'' 8P!-"45Jm0JJJKKK ''{CCCC 	m6mmFT\):):FFFGGG 	LJD,=(>(>JJJKKKmmmr   rN   rO   c           
      N   	 d|j          dt          j                            |          }||j        t          d|           t          j                            |          }|t          j        <   |j        	                    |           fdt          j        |t          j                  D             }|s)d| j        |j         <   t          d|j          d           dS |D ]\  }}|                     ||          }|r-|| j        |j         <   t          d	|j          d
| d|            J	  |            }	|	j        j         }
|
| j        v rt          d|j          d
| d|
 d           |	| j        |
<   t          d|j          d
| d|
 d           # t$          $ r9}d| | j        |j         <   t          d	|j          d
| d|            Y d}~d}~ww xY wdS # t$          $ r7}d| | j        |j         <   t          d	|j          d|            Y d}~dS d}~ww xY w)z
        Load a single plugin from a plugin.py file.
        
        Args:
            plugin_folder: The plugin's directory
            plugin_file: Path to the plugin.py file
        zplugins.z.pluginNzCould not load spec for c                 ^    g | ])\  }}|                     d           r|j        k    %||f*S )Plugin)endswithr!   )rB   r
   objmodule_names      r   rD   z9PluginRegistry._load_plugin_from_file.<locals>.<listcomp>   sL        )c==**/2~/L/L s/L/L/Lr   zNo *Plugin class foundrE   z%: No class ending with 'Plugin' foundu   ❌ .z: u   ⚠️  z: Duplicate plugin name 'z' (skipping)u   ✅ u    → ''zFailed to instantiate: z: Failed to instantiate: zImport failed: z: Import failed: )r
   	importlibutilspec_from_file_locationloaderImportErrormodule_from_specsysmodulesexec_moduleinspect
getmembersisclassr8   rH   _validate_plugin_classmetadatar5   	Exception)r   rN   rO   specmoduleplugin_classes
class_nameplugin_classvalidation_errorplugin_instanceplugin_nameerV   s               @r   rK   z%PluginRegistry._load_plugin_from_file   s   3	C@]%7@@@K>99+{SSD|t{2!"J["J"JKKK^44T::F'-CK$K##F+++   -4-?-X-X  N
 " 8P!-"45Wm0WWWXXX -; ` `(
L#'#>#>|Z#X#X # <LD%m&89V!3VVjVVDTVVWWW`&2lnnO"1":"?K #dl22|);||j||cn|||}}} 0?DL-V!3VVjVVVVVWWWW  ` ` `<YVW<Y<YD%m&89^!3^^j^^[\^^________`+` `2  	C 	C 	C4Ia4I4IDm01A+AAaAABBBBBBBBB	CsU   C"G# 'AG# 4=F1G# 2(FG# 
G%/GG# GG# #
H$-,HH$rl   rk   c                    t          |d          sdS t          |d          sdS t          |d          }t          j        |          sdS 	 |j        }t          |d          r|j        sdS t          |d          r|j        sd	S t          d
 |j        D                       sd|j         dS n# t          $ r}d| cY d}~S d}~ww xY wdS )a"  
        Validate that a plugin class follows the required structure.
        
        Args:
            plugin_class: The class to validate
            class_name: Name of the class (for error messages)
        
        Returns:
            Error message if invalid, None if valid
        rf   zMissing 'metadata' attributeexecutezMissing 'execute' methodz'execute' method must be asyncr
   zmetadata.name is requiredr-   z metadata.description is requiredc              3   F   K   | ]}|                                 p|d v V  dS )z-_N)isalnum)rB   cs     r   	<genexpr>z8PluginRegistry._validate_plugin_class.<locals>.<genexpr>   s3      GGAqyy{{/a4iGGGGGGr   zmetadata.name 'z?' is not URL-safe (use alphanumeric, hyphens, underscores only)zInvalid metadata: N)	hasattrgetattrrb   iscoroutinefunctionrf   r
   r-   allrg   )r   rl   rk   execute_methodrf   rp   s         r   re   z%PluginRegistry._validate_plugin_class   s4    |Z00 	211 |Y// 	.-- !y99*>:: 	433	,#,H 8V,, 3HM 3228]33 :8;O :99 GGGGGGG xwwwwwx  	, 	, 	,+++++++++	, ts*   B/ ,B/ (B/ /
C9C=CCr
   c                 6    | j                             |          S )z
        Get a plugin instance by name.
        
        Args:
            name: The plugin name (from metadata.name)
        
        Returns:
            Plugin instance or None if not found
        )r5   get)r   r
   s     r   
get_pluginzPluginRegistry.get_plugin   s     |%%%r   c                 4    | j                                         S )z
        Get all loaded plugins.
        
        Returns:
            Dictionary mapping plugin names to plugin instances
        )r5   copyr   s    r   list_pluginszPluginRegistry.list_plugins   s     |  """r   c                 4    | j                                         S )z
        Get all plugin load errors.
        
        Returns:
            Dictionary mapping plugin folder names to error messages
        )r8   r   r   s    r   get_load_errorszPluginRegistry.get_load_errors   s      %%'''r   r5   )r:   N)r    r!   r"   r#   r$   r9   rP   r   rK   r   r   re   r   r~   r   r   r   r*   r   r   r4   r4   :   s         	/ 	/C 	/ 	/ 	/ 	/0 0 0 0d;CD ;Ct ;CPT ;C ;C ;C ;Cz*4 *S *XVY] * * * *X
&s 
&x} 
& 
& 
& 
&#d38n # # # #(c3h ( ( ( ( ( (r   r4   _plugin_registryr:   c                  :    t           t                      a t           S )zo
    Get the global plugin registry instance.
    
    Returns:
        The global PluginRegistry instance
    )r   r4   r*   r   r   get_plugin_registryr     s     )++r   r5   r6   c                 `    t          |           at                                           t          S )z
    Initialize the plugin system by discovering and loading all plugins.
    
    Args:
        plugins_dir: Path to the plugins directory
    
    Returns:
        The initialized PluginRegistry instance
    )r4   r   rP   )r6   s    r   initialize_pluginsr     s*     &k22&&(((r   r   )r#   importlib.utilrY   rb   r_   pathlibr   typingr   r   r   dataclassesr   r	   r,   r4   r   r%   r   r$   r   r*   r   r   <module>r      ss          



       & & & & & & & & & & ! ! ! ! ! !        0 ! ! ! ! ! ! ! !J( J( J( J( J( J( J( J(\ .2 (>* 1 1 1
^ 
 
 
 
 C       r   