Ë
    ™·Ki±7  ã                  ó”  — U d Z ddlmZ ddlZddlmZmZ ddlmZ ddl	m
Z
mZmZmZmZmZ ddlmZmZ  ej&                  e«      Z G d	„ d
e«      Z ed¬«       G d„ d«      «       Ze G d„ d«      «       Z G d„ de«      Ze G d„ d«      «       Z G d„ d«      Z G d„ d«      Zdaded<   dd„Z	 	 d 	 	 	 	 	 	 	 	 	 	 	 d!d„Z d"d„Z!d#d„Z"y)$z Core hook system implementation.é    )ÚannotationsN)Ú	dataclassÚfield)ÚEnum)ÚAnyÚCallableÚDictÚListÚOptionalÚProtocolé   )ÚHookExecutionErrorÚHookRejectionErrorc                  ó   — e Zd ZdZdZdZy)ÚHookTypezHook execution timing types.ÚpreÚpostN)Ú__name__Ú
__module__Ú__qualname__Ú__doc__ÚPREÚPOST© ó    úb/home/cursorai/projects/django-wallet-utils/venv/lib/python3.12/site-packages/django_hooks/core.pyr   r      s   „ Ù&à
€CØ�Dr   r   T)Úfrozenc                  ó8   — e Zd ZU dZded<    ee¬«      Zded<   y)ÚHookContexta¹  
    Immutable context passed to hooks containing operation details.

    This is a base class that should be extended by specific implementations
    to add domain-specific fields.

    All operation parameters are frozen to prevent hooks from modifying them.
    Use the metadata dict to share data between hooks.

    Attributes:
        operation: The operation being performed (e.g., 'add', 'delete', 'execute')
        metadata: Mutable dictionary for inter-hook communication and data sharing

    Example:
        @dataclass(frozen=True)
        class CronjobHookContext(HookContext):
            job_id: str
            schedule: str
            command: str
            user_id: int
    ÚstrÚ	operation)Údefault_factoryúDict[str, Any]ÚmetadataN)r   r   r   r   Ú__annotations__r   Údictr$   r   r   r   r   r      s   … ñð, ƒNÙ$°TÔ:€HˆnÔ:r   r   c                  óD   — e Zd ZU dZded<   ded<   ded<   ded<   ded	<   y
)ÚPostHookErroraþ  
    Represents an error that occurred in a POST hook.

    POST hooks cannot reject operations, but their errors are captured
    and returned so they can be logged or displayed without affecting
    the operation's success.

    Attributes:
        hook_name: Name of the hook that raised the error
        error_code: Machine-readable error code (ALL_CAPS)
        message: Human-readable error message
        details: Additional error context
        exception: The original exception that was raised
    r    Ú	hook_nameÚ
error_codeÚmessager#   ÚdetailsÚ	ExceptionÚ	exceptionN)r   r   r   r   r%   r   r   r   r(   r(   2   s$   … ñð ƒNØƒOØƒLØÓØÔr   r(   c                  ó   — e Zd ZdZdd„Zy)ÚHookFunctionz3Protocol defining the signature for hook functions.c                 ó   — y)aÑ  
        Execute hook logic.

        Args:
            context: Immutable operation context with mutable metadata

        Returns:
            - For PRE hooks: True to continue, False to reject, or raise HookRejectionError
            - For POST hooks: Return value is ignored

        Raises:
            HookRejectionError: To reject operation with error code (PRE hooks only)
            Exception: Any other exception is wrapped in HookExecutionError
        Nr   )ÚselfÚcontexts     r   Ú__call__zHookFunction.__call__M   s   € ð 	r   N)r3   r   Úreturnzbool | None)r   r   r   r   r4   r   r   r   r0   r0   J   s
   „ Ù=ôr   r0   c                  óH   — e Zd ZU dZded<   ded<   ded<   ded<   d	Zd
ed<   y)ÚHookaW  
    Represents a registered hook.

    Attributes:
        name: Unique identifier for the hook
        hook_type: When the hook executes (PRE or POST)
        operation: Which operation triggers this hook ('*' = all operations)
        callback: The function to execute
        priority: Execution order (lower number = higher priority)
    r    Únamer   Ú	hook_typer!   r0   Úcallbackéd   ÚintÚpriorityN)r   r   r   r   r%   r=   r   r   r   r7   r7   _   s)   … ñ	ð ƒIØÓØƒNØÓØ€HˆcÔr   r7   c                  óV   — e Zd ZdZdd	d„Z	 	 d
	 	 	 	 	 	 	 	 	 	 	 dd„Zdd„Zdd„Zdd„Zy)ÚHookRegistryaÒ  
    Registry for managing hooks with priority support.

    Hooks are stored per operation type and executed by priority (lower = higher priority).

    Example:
        registry = HookRegistry(operations=['schedule', 'execute', 'pause', 'delete'])
        registry.register(
            name='check_permissions',
            hook_type=HookType.PRE,
            callback=check_permissions_func,
            operation='delete',
            priority=10
        )
    Nc                óH   — dg i| _         |r|D ]  }g | j                   |<   Œ yy)zŒ
        Initialize hook registry.

        Args:
            operations: List of valid operation names. If None, defaults to ['*']
        Ú*N)Ú_hooks)r2   Ú
operationsÚops      r   Ú__init__zHookRegistry.__init__„   s.   € ð /2°2¨YˆŒáÛ �Ø"$�—‘˜B’ñ !ð r   c           
     ó  ‡— || j                   vr8t        d|› ddj                  | j                   j                  «       «      › �«      ‚| j                   j	                  «       D ]%  }t        ˆfd„|D «       «      sŒt        d‰› d�«      ‚ t        ‰||||¬«      }| j                   |   j                  |«       | j                   |   j                  d„ ¬	«       t        j                  d
|j                  › d‰› d|› d|› �«       y)a½  
        Register a new hook.

        Args:
            name: Unique identifier for the hook
            hook_type: HookType.PRE or HookType.POST
            callback: Function to execute
            operation: Operation name or '*' for all operations
            priority: Execution priority (lower number = higher priority, default=100)

        Raises:
            ValueError: If operation is invalid or hook name already exists

        Example:
            registry.register(
                name="check_daily_limit",
                hook_type=HookType.PRE,
                callback=check_daily_limit_hook,
                operation="execute",
                priority=50,
            )
        zInvalid operation: z. Must be one of: z, c              3  ó<   •K  — | ]  }|j                   ‰k(  –— Œ y ­w©N©r8   )Ú.0Úhr8   s     €r   Ú	<genexpr>z(HookRegistry.register.<locals>.<genexpr>¶   s   øè ø€ Ð1©5 a�1—6‘6˜T•>©5ùs   ƒzHook with name 'z' already registered)r8   r9   r!   r:   r=   c                ó2   — | j                   | j                  fS rH   ©r=   r8   ©rK   s    r   Ú<lambda>z'HookRegistry.register.<locals>.<lambda>Ã   s   € °1·:±:¸q¿v¹vÑ2Fr   ©ÚkeyzRegistered z hook 'z' for operation 'z' with priority N)rB   Ú
ValueErrorÚjoinÚkeysÚvaluesÚanyr7   ÚappendÚsortÚloggerÚinfoÚvalue)r2   r8   r9   r:   r!   r=   ÚhooksÚhooks    `      r   ÚregisterzHookRegistry.register‘   s  ø€ ð< ˜DŸK™KÑ'ÜØ% i [Ð0BÀ4Ç9Á9ÈTÏ[É[×M]ÑM]ÓM_ÓC`ÐBaÐbóð ð
 —[‘[×'Ñ'Ö)ˆEÜÓ1©5Ó1Õ1Ü Ð#3°D°6Ð9MÐ!NÓOÐOð *ô ØØØØØô
ˆð 	�‰�IÑ×%Ñ% dÔ+à�‰�IÑ×#Ñ#Ñ(FÐ#ÔGä�‰Ø˜)Ÿ/™/Ð*¨'°$°Ð7HÈÈÐScÐdlÐcmÐnõ	
r   c           	     óÔ   — | j                   j                  «       D ]K  \  }}|D ]A  }|j                  |k(  sŒ|j                  |«       t        j                  d|› d|› d�«         y ŒM y)z³
        Unregister a hook by name.

        Args:
            name: Hook name to remove

        Returns:
            True if hook was found and removed, False otherwise
        zUnregistered hook 'z' from operation 'Ú'TF)rB   Úitemsr8   ÚremoverZ   r[   )r2   r8   r!   r]   r^   s        r   Ú
unregisterzHookRegistry.unregisterÉ   sh   € ð !%§¡× 1Ñ 1Ö 3ÑˆI�uÛ�Ø—9‘9 Ó$Ø—L‘L Ô&Ü—K‘KÐ"5°d°VÐ;MÈiÈ[ÐXYÐ ZÔ[Úñ	 ð !4ð r   c                óî   — | j                   j                  |g «      }| j                   j                  dg «      }||z   }|D �cg c]  }|j                  |k(  sŒ|‘Œ }}|j                  d„ ¬«       |S c c}w )a  
        Get all hooks for a specific operation and type, sorted by priority.

        Args:
            operation: Operation name
            hook_type: HookType.PRE or HookType.POST

        Returns:
            List of hooks sorted by priority (lower number first)
        rA   c                ó2   — | j                   | j                  fS rH   rN   rO   s    r   rP   z(HookRegistry.get_hooks.<locals>.<lambda>ï   s   €  Q§Z¡Z°·±Ñ$8r   rQ   )rB   Úgetr9   rY   )r2   r!   r9   Úoperation_hooksÚglobal_hooksÚ	all_hooksrK   Úfiltereds           r   Ú	get_hookszHookRegistry.get_hooksÛ   ss   € ð Ÿ+™+Ÿ/™/¨)°RÓ8ˆØ—{‘{—‘ s¨BÓ/ˆð $ lÑ2ˆ	Ù(ÓE™y˜!¨A¯K©K¸9Ó,D’A˜yˆÐEð 	�‰Ñ8ˆÔ9àˆùò Fs   ÁA2ÁA2c                óŠ   — | j                   D ]  }| j                   |   j                  «        Œ! t        j                  d«       y)z0Clear all registered hooks (useful for testing).zCleared all hooksN)rB   ÚclearrZ   r[   )r2   r!   s     r   rn   zHookRegistry.clearó   s2   € àŸœˆIØ�K‰K˜	Ñ"×(Ñ(Õ*ð %ä�‰Ð'Õ(r   rH   )rC   zOptional[List[str]]©rA   r;   ©r8   r    r9   r   r:   r0   r!   r    r=   r<   r5   ÚNone©r8   r    r5   Úbool)r!   r    r9   r   r5   z
List[Hook]©r5   rq   )	r   r   r   r   rE   r_   rd   rl   rn   r   r   r   r?   r?   s   sc   „ ñô %ð$ Øð6
àð6
ð ð6
ð ð	6
ð
 ð6
ð ð6
ð 
ó6
ópó$ô0)r   r?   c                  ó*   — e Zd ZdZddd„Zdd„Zd	d„Zy)
ÚHookManagera«  
    Manages hook execution for operations.

    Executes PRE hooks before operation (can reject) and POST hooks after (cannot reject).

    Example:
        manager = HookManager(registry)
        
        # Before operation
        context = MyHookContext(operation='delete', user_id=123)
        try:
            manager.execute_pre_hooks(context)
        except HookRejectionError as e:
            return {"error": e.error_code, "message": e.message}
        
        # Perform operation
        result = perform_operation()
        
        # After operation
        errors = manager.execute_post_hooks(context)
        return {"success": True, "post_hook_errors": errors}
    Nc                ó*   — |xs
 t        «       | _        y)z„
        Initialize hook manager.

        Args:
            registry: Hook registry to use. If None, uses global registry.
        N)r?   Úregistry)r2   rx   s     r   rE   zHookManager.__init__  s   € ð !Ò2¤L£Nˆ�r   c           
     ó´  — | j                   j                  |j                  t        j                  «      }|D ]¬  }t
        j                  d|j                  › d|j                  › �«       	 |j                  |«      }|du rBt        d|j                  j                  «       › �d|j                  › �d|j                  i¬«      ‚t
        j                  d|j                  › d	�«       Œ® y# t        $ r ‚ t        $ r^}t
        j                  d|j                  › d
|› �d¬«       t        d|j                  › dt        |«      › �|j                  |¬«      |‚d}~ww xY w)a“  
        Execute all PRE hooks for the given operation.

        PRE hooks can reject the operation by:
        1. Returning False
        2. Raising HookRejectionError with error_code and message

        Args:
            context: Operation context

        Raises:
            HookRejectionError: If any hook rejects the operation
            HookExecutionError: If a hook fails unexpectedly
        zExecuting PRE hook 'ú' for FÚHOOK_REJECTED_zOperation rejected by hook: r)   )r*   r+   r,   z
PRE hook 'ú' completed successfullyú' failed with error: T©Úexc_infoúHook 'z' failed to execute: )r+   r)   Úoriginal_exceptionN)rx   rl   r!   r   r   rZ   Údebugr8   r:   r   Úupperr-   Úerrorr   r    )r2   r3   r]   r^   ÚresultÚes         r   Úexecute_pre_hookszHookManager.execute_pre_hooks  s=  € ð —‘×'Ñ'¨×(9Ñ(9¼8¿<¹<ÓHˆãˆDÜ�L‰LÐ/°·	±	¨{¸&À×ARÑARÐ@SÐTÔUðØŸ™ wÓ/�ð ˜U‘?Ü,Ø%3°D·I±I·O±OÓ4EÐ3FÐ#GØ">¸t¿y¹y¸kÐ JØ!,¨d¯i©iÐ 8ôð ô —‘˜z¨$¯)©)¨Ð4LÐMÕNñ øô  &ò àäò ä—‘˜z¨$¯)©)¨Ð4IÈ!ÈÐMÐX\�Ô]Ü(Ø$ T§Y¡Y KÐ/DÄSÈÃVÀHÐMØ"Ÿi™iØ'(ôð ð	ûðús   Á*A:C'Ã'EÃ9AEÅEc                óš  — | j                   j                  |j                  t        j                  «      }g }|D ]f  }t
        j                  d|j                  › d|j                  › �«       	 |j                  |«       t
        j                  d|j                  › d�«       Œh |S # t        $ r�}t
        j                  d|j                  › d|j                  › d|j                  › �«       |j                  t        |j                  |j                  |j                  |j                  |¬«      «       Y d}~Œüd}~wt         $ r‰}t
        j#                  d|j                  › d	|› �d
¬«       |j                  t        |j                  dd|j                  › dt%        |«      › �dt'        |«      j(                  i|¬«      «       Y d}~�ŒŒd}~ww xY w)aª  
        Execute all POST hooks for the given operation.

        POST hooks cannot reject the operation. Errors are captured and returned
        so the operation remains successful but errors can be logged/displayed.

        Args:
            context: Operation context (typically with updated state after operation)

        Returns:
            List of errors that occurred in POST hooks (empty if all succeeded)
        zExecuting POST hook 'rz   zPOST hook 'r|   z' tried to reject operation: z - )r)   r*   r+   r,   r.   Nr}   Tr~   ÚHOOK_EXECUTION_ERRORr€   z
' failed: Úexception_type)rx   rl   r!   r   r   rZ   r‚   r8   r:   r   Úwarningr*   r+   rX   r(   r,   r-   r„   r    Útyper   )r2   r3   r]   Úerrorsr^   r†   s         r   Úexecute_post_hookszHookManager.execute_post_hooksI  s‰  € ð —‘×'Ñ'¨×(9Ñ(9¼8¿=¹=ÓIˆØ&(ˆãˆDÜ�L‰LÐ0°·±°¸6À'×BSÑBSÐATÐUÔVðØ—‘˜gÔ&Ü—‘˜{¨4¯9©9¨+Ð5MÐNÕOð ðF ˆøô9 &ò ä—‘Ø! $§)¡) Ð,IÈ!Ï,É,ÈÐWZÐ[\×[dÑ[dÐZeÐfôð —‘Ü!Ø"&§)¡)Ø#$§<¡<Ø !§	¡	Ø !§	¡	Ø"#ô÷ñ ûô ò ä—‘˜{¨4¯9©9¨+Ð5JÈ1È#ÐNÐY]�Ô^Ø—‘Ü!Ø"&§)¡)Ø#9Ø"(¨¯©¨°:¼cÀ!»f¸XÐ FØ!1´4¸³7×3CÑ3CÐ DØ"#ô÷ò ûðús&   Á,4B$Â$	G
Â-BD5Ä5G
ÅA>GÇG
rH   )rx   úOptional[HookRegistry])r3   r   r5   rq   )r3   r   r5   zList[PostHookError])r   r   r   r   rE   r‡   rŽ   r   r   r   rv   rv   ú   s   „ ñô.3ó,ô\3r   rv   r�   Ú_global_registryc                 ó.   — t         €
t        «       a t         S )zd
    Get the global hook registry.

    Lazily initializes the global registry on first access.
    )r�   r?   r   r   r   Úget_global_registryr’   ƒ  s   € ô ÐÜ'›>ÐÜÐr   c                ó>   — t        «       j                  | ||||«       y)aR  
    Register a hook in the global registry.

    Args:
        name: Unique identifier for the hook
        hook_type: HookType.PRE or HookType.POST
        callback: Function to execute
        operation: Operation name or '*' for all operations
        priority: Execution priority (lower number = higher priority, default=100)

    Example:
        def check_quota(context: HookContext) -> bool:
            if context.metadata.get('count', 0) > 100:
                raise HookRejectionError(
                    error_code="QUOTA_EXCEEDED",
                    message="You have exceeded your quota limit"
                )
            return True

        register_hook(
            name="quota_check",
            hook_type=HookType.PRE,
            callback=check_quota,
            operation="create",
            priority=10,
        )
    N)r’   r_   )r8   r9   r:   r!   r=   s        r   Úregister_hookr”   �  s   € ôD Ó×"Ñ" 4¨°H¸iÈÕRr   c                ó4   — t        «       j                  | «      S )z¬
    Unregister a hook from the global registry.

    Args:
        name: Hook name to remove

    Returns:
        True if hook was found and removed, False otherwise
    )r’   rd   rI   s    r   Úunregister_hookr–   ´  s   € ô Ó ×+Ñ+¨DÓ1Ð1r   c                 ó4   — t        «       j                  «        y)z>Clear all hooks from the global registry (useful for testing).N)r’   rn   r   r   r   Úclear_hooksr˜   Á  s   € äÓ×ÑÕ!r   )r5   r?   ro   rp   rr   rt   )#r   Ú
__future__r   ÚloggingÚdataclassesr   r   Úenumr   Útypingr   r   r	   r
   r   r   Ú
exceptionsr   r   Ú	getLoggerr   rZ   r   r   r(   r0   r7   r?   rv   r�   r%   r’   r”   r–   r˜   r   r   r   Ú<module>r       s  ðÚ &å "ã ß (Ý ß @× @ç >à	ˆ×	Ñ	˜8Ó	$€ôˆtô ñ �$Ô÷;ð ;ó ð;ð6 ÷ð ó ðô.�8ô ð* ÷ð ó ð÷&D)ñ D)÷NBñ BðL ,0Ð Ð(Ó /ó	ð  Øð"SØ
ð"Sàð"Sð ð"Sð ð	"Sð
 ð"Sð 
ó"SóJ
2ô"r   