
    AҐi5                     F   d Z ddlZddlZddlmZ ddlmZmZmZm	Z	m
Z
mZmZmZmZmZmZmZ  G d de      Z G d d	e      Z G d
 de      Z G d d      Z G d d      Z ej.                         Zdad Zd Zd Zd ZddZd ZddZ d Z!d Z"d Z#ddZ$d Z%d Z&d Z'y)aL  
Stash - Global Shared State for Dirty Workers

Provides simple key-value tables stored in the arbiter process.
All workers can read and write to the same tables.

Usage::

    from gunicorn.dirty import stash

    # Basic operations - table is auto-created on first access
    stash.put("sessions", "user:1", {"name": "Alice", "role": "admin"})
    user = stash.get("sessions", "user:1")
    stash.delete("sessions", "user:1")

    # Dict-like interface
    sessions = stash.table("sessions")
    sessions["user:1"] = {"name": "Alice"}
    user = sessions["user:1"]
    del sessions["user:1"]

    # Query operations
    keys = stash.keys("sessions")
    keys = stash.keys("sessions", pattern="user:*")

    # Table management
    stash.ensure("cache")           # Explicit creation (idempotent)
    stash.clear("sessions")         # Delete all entries
    stash.delete_table("sessions")  # Delete the table itself
    tables = stash.tables()         # List all tables

Declarative usage in DirtyApp::

    class MyApp(DirtyApp):
        stashes = ["sessions", "cache"]  # Auto-created on arbiter start

        def __call__(self, action, *args, **kwargs):
            # Tables are ready to use
            stash.put("sessions", "key", "value")

Note: Tables are stored in the arbiter process and are ephemeral.
If the arbiter restarts, all data is lost.
    N   )
DirtyError)DirtyProtocolSTASH_OP_PUTSTASH_OP_GETSTASH_OP_DELETESTASH_OP_KEYSSTASH_OP_CLEARSTASH_OP_INFOSTASH_OP_ENSURESTASH_OP_DELETE_TABLESTASH_OP_TABLESSTASH_OP_EXISTSmake_stash_messagec                       e Zd ZdZy)
StashErrorz$Base exception for stash operations.N)__name__
__module____qualname____doc__     M/var/www/descvideos/venv/lib/python3.12/site-packages/gunicorn/dirty/stash.pyr   r   E   s    .r   r   c                   "     e Zd ZdZ fdZ xZS )StashTableNotFoundErrorz#Raised when a table does not exist.c                 8    || _         t        | 	  d|        y )NzStash table not found: )
table_namesuper__init__)selfr   	__class__s     r   r   z StashTableNotFoundError.__init__L   s    $2:,?@r   r   r   r   r   r   __classcell__r!   s   @r   r   r   I   s    -A Ar   r   c                   "     e Zd ZdZ fdZ xZS )StashKeyNotFoundErrorz,Raised when a key does not exist in a table.c                 L    || _         || _        t        |   d| d|        y )NzKey not found in z: )r   keyr   r   )r    r   r(   r!   s      r   r   zStashKeyNotFoundError.__init__T   s,    $,ZL3%@Ar   r"   r$   s   @r   r&   r&   Q   s    6B Br   r&   c                       e Zd ZdZddZd Zd Zd ZddZd Z	dd	Z
d
 ZddZd Zd Zd ZddZd Zd Zd Zd Zd Zd Zy)StashClientzl
    Client for stash operations.

    Communicates with the arbiter which stores all tables in memory.
    c                 `    || _         || _        d| _        t        j                         | _        y)z
        Initialize the stash client.

        Args:
            socket_path: Path to the dirty arbiter's Unix socket
            timeout: Default timeout for operations in seconds
        N)socket_pathtimeout_sock	threadingLock_lock)r    r,   r-   s      r   r   zStashClient.__init__a   s(     '
^^%
r   c                 <    t        t        j                               S )zGenerate a unique request ID.)struuiduuid4r    s    r   _get_request_idzStashClient._get_request_idn   s    4::<  r   c                 x   ddl }| j                  y	 |j                  |j                  |j                        | _        | j                  j	                  | j
                         | j                  j                  | j                         y# |j                  t        f$ r}d| _        t        d|       |d}~ww xY w)z Establish connection to arbiter.r   NzFailed to connect to arbiter: )socketr.   AF_UNIXSOCK_STREAM
settimeoutr-   connectr,   errorOSErrorr   )r    r9   es      r   _connectzStashClient._connectr   s    ::!	Jv~~v7I7IJDJJJ!!$,,/JJt//0g& 	JDJ=aSABI	Js   A5B	 	B9B44B9c                     | j                   #	 | j                   j                          d| _         yy# t        $ r Y w xY w)zClose the connection.N)r.   close	Exceptionr6   s    r   _closezStashClient._close   sC    ::!

  " DJ "  s   1 	==Nc           	      2   | j                   5  | j                  | j                          | j                         }t	        ||||||      }	 t        j                  | j                  |       t        j                  | j                        }|j                  d      }	|	t
        j                  k(  r|j                  d      cddd       S |	t
        j                  k(  rb|j                  di       }
|
j                  dd      }|
j                  dd	      }|d
k(  rt        |      |dk(  rt        ||      t        |      t        d|	       # t        $ r5}| j                          t!        |t              r t        d|       |d}~ww xY w# 1 sw Y   yxY w)a6  
        Execute a stash operation.

        Args:
            op: Operation code (STASH_OP_*)
            table: Table name
            key: Optional key
            value: Optional value
            pattern: Optional pattern for keys operation

        Returns:
            Result from the operation
        N)r(   valuepatterntyperesultr>   
error_typer   messagezUnknown errorr   r&   zUnexpected response type: zStash operation failed: )r1   r.   rA   r7   r   r   write_messageread_messagegetMSG_TYPE_RESPONSEMSG_TYPE_ERRORr   r&   r   rD   rE   
isinstance)r    optabler(   rG   rH   
request_idrL   responsemsg_type
error_inforK   	error_msgr@   s                 r   _executezStashClient._execute   s~    ZZ "	Hzz!--/J(BugG
H++DJJ@(55djjA#<</}>>>#<<1!"	H "	H" !=!==!)gr!:J!+l!KJ *y/ JI!%>>5e<<!%<<3E3??$Y//$'A(%LMM Ha, #;A3!?@aG	H="	H "	Hs1   >FA3E	BE	F
0FF

FFc                 6    | j                  t        |||       y)z
        Store a value in a table.

        The table is automatically created if it doesn't exist.

        Args:
            table: Table name
            key: Key to store under
            value: Value to store (must be serializable)
        )r(   rG   N)rZ   r   )r    rT   r(   rG   s       r   putzStashClient.put   s     	lEs%@r   c                 V    	 | j                  t        ||      S # t        $ r |cY S w xY w)z
        Retrieve a value from a table.

        Args:
            table: Table name
            key: Key to retrieve
            default: Default value if key not found

        Returns:
            The stored value, or default if not found
        r(   )rZ   r   r&   )r    rT   r(   defaults       r   rO   zStashClient.get   s0    	==u#=>>$ 	N	s    ((c                 2    | j                  t        ||      S )z
        Delete a key from a table.

        Args:
            table: Table name
            key: Key to delete

        Returns:
            True if key was deleted, False if it didn't exist
        r^   )rZ   r   r    rT   r(   s      r   deletezStashClient.delete        }}_e}==r   c                 2    | j                  t        ||      S )z
        Get all keys in a table, optionally filtered by pattern.

        Args:
            table: Table name
            pattern: Optional glob pattern (e.g., "user:*")

        Returns:
            List of keys
        rH   )rZ   r	   )r    rT   rH   s      r   keyszStashClient.keys   s     }}]E7}CCr   c                 0    | j                  t        |       y)z]
        Delete all entries in a table.

        Args:
            table: Table name
        N)rZ   r
   r    rT   s     r   clearzStashClient.clear   s     	ne,r   c                 .    | j                  t        |      S )z
        Get information about a table.

        Args:
            table: Table name

        Returns:
            Dict with table info (size, etc.)
        )rZ   r   rh   s     r   infozStashClient.info   s     }}]E22r   c                 0    | j                  t        |       y)z
        Ensure a table exists (create if not exists).

        This is idempotent - calling it multiple times is safe.

        Args:
            table: Table name
        N)rZ   r   rh   s     r   ensurezStashClient.ensure  s     	ou-r   c                 2    | j                  t        ||      S )z
        Check if a table or key exists.

        Args:
            table: Table name
            key: Optional key to check within the table

        Returns:
            True if exists, False otherwise
        r^   )rZ   r   ra   s      r   existszStashClient.exists  rc   r   c                 0    | j                  t        |       y)zV
        Delete an entire table.

        Args:
            table: Table name
        N)rZ   r   rh   s     r   delete_tablezStashClient.delete_table$  s     	+U3r   c                 .    | j                  t        d      S )zT
        List all tables.

        Returns:
            List of table names
         )rZ   r   r6   s    r   tableszStashClient.tables-  s     }}_b11r   c                     t        | |      S )z
        Get a dict-like interface to a table.

        Args:
            name: Table name

        Returns:
            StashTable instance
        )
StashTable)r    names     r   rT   zStashClient.table6  s     $%%r   c                 f    | j                   5  | j                          ddd       y# 1 sw Y   yxY w)zClose the client connection.N)r1   rE   r6   s    r   rC   zStashClient.closeB  s'    ZZ 	KKM	 	 	s   '0c                     | S Nr   r6   s    r   	__enter__zStashClient.__enter__G  s    r   c                 $    | j                          y rz   )rC   )r    exc_typeexc_valexc_tbs       r   __exit__zStashClient.__exit__J  s    

r   )g      >@)NNNrz   )r   r   r   r   r   r7   rA   rE   rZ   r\   rO   rb   rf   ri   rk   rm   ro   rq   rt   rT   rC   r{   r   r   r   r   r*   r*   Z   sk    &!J0HlA">D-
3	.>42
&
r   r*   c                   l    e Zd ZdZd Zed        Zd Zd Zd Z	d Z
d Zd	 ZddZddZd Zd Zd Zy
)rv   a2  
    Dict-like interface to a stash table.

    Example::

        sessions = stash.table("sessions")
        sessions["user:1"] = {"name": "Alice"}
        user = sessions["user:1"]
        del sessions["user:1"]

        # Iteration
        for key in sessions:
            print(key, sessions[key])
    c                      || _         || _        y rz   )_client_name)r    clientrw   s      r   r   zStashTable.__init__^  s    
r   c                     | j                   S )zTable name.)r   r6   s    r   rw   zStashTable.nameb  s     zzr   c                     | j                   j                  | j                  |      }|1| j                   j                  | j                  |      st	        |      |S rz   )r   rO   r   ro   KeyError)r    r(   rJ   s      r   __getitem__zStashTable.__getitem__g  sH    !!$**c2><<&&tzz37sm#r   c                 R    | j                   j                  | j                  ||       y rz   )r   r\   r   )r    r(   rG   s      r   __setitem__zStashTable.__setitem__o  s    S%0r   c                 f    | j                   j                  | j                  |      st        |      y rz   )r   rb   r   r   r    r(   s     r   __delitem__zStashTable.__delitem__r  s)    ||""4::s33- 4r   c                 N    | j                   j                  | j                  |      S rz   )r   ro   r   r   s     r   __contains__zStashTable.__contains__v  s    ||""4::s33r   c                 ^    t        | j                  j                  | j                              S rz   )iterr   rf   r   r6   s    r   __iter__zStashTable.__iter__y  s     DLL%%djj122r   c                 p    | j                   j                  | j                        }|j                  dd      S )Nsizer   )r   rk   r   rO   )r    rk   s     r   __len__zStashTable.__len__|  s+    ||  ,xx""r   Nc                 P    | j                   j                  | j                  ||      S )zGet value with default.)r   rO   r   )r    r(   r_   s      r   rO   zStashTable.get  s    ||

C99r   c                 P    | j                   j                  | j                  |      S )z-Get all keys, optionally filtered by pattern.re   )r   rf   r   )r    rH   s     r   rf   zStashTable.keys  s     ||  W ==r   c                 N    | j                   j                  | j                         y)zDelete all entries.N)r   ri   r   r6   s    r   ri   zStashTable.clear  s    4::&r   c              #      K   | j                   j                  | j                        D ],  }|| j                   j                  | j                  |      f . yw)z Iterate over (key, value) pairs.Nr   rf   r   rO   r   s     r   itemszStashTable.items  sH     <<$$TZZ0 	9Ct||''

C888	9s   AAc              #      K   | j                   j                  | j                        D ]*  }| j                   j                  | j                  |       , yw)zIterate over values.Nr   r   s     r   valueszStashTable.values  sC     <<$$TZZ0 	4C,,""4::s33	4s   AArz   )r   r   r   r   r   propertyrw   r   r   r   r   r   r   rO   rf   ri   r   r   r   r   r   rv   rv   N  sW      1 43#:>'9
4r   rv   c                     | a y)z@Set the global stash socket path (called during initialization).N)_stash_socket_path)paths    r   set_stash_socket_pathr     s
     r   c                  v    ddl } t        *| j                  j                  d      }|r|S t	        d      t        S )zGet the stash socket path.r   NGUNICORN_DIRTY_SOCKETz\Stash socket path not configured. Make sure dirty_workers > 0 and dirty_apps are configured.)osr   environrO   r   )r   r   s     r   get_stash_socket_pathr     sA    !zz~~56KI
 	
 r   c                  l    t        t        dd      } |  t               }t        |      } | t        _        | S )z*Get or create a thread-local stash client.stash_clientN)getattr_thread_localr   r*   r   )r   r,   s     r   _get_clientr     s4    ]ND9F~+-[)%+"Mr   c                 :    t               j                  | ||       y)zStore a value in a table.N)r   r\   )rT   r(   rG   s      r   r\   r\     s    MeS%(r   c                 8    t               j                  | ||      S )zRetrieve a value from a table.)r   rO   )rT   r(   r_   s      r   rO   rO     s    =UC11r   c                 6    t               j                  | |      S )zDelete a key from a table.)r   rb   rT   r(   s     r   rb   rb         =s++r   c                 6    t               j                  | |      S )zGet all keys in a table.)r   rf   )rT   rH   s     r   rf   rf     s    =eW--r   c                 6    t               j                  |        y)zDelete all entries in a table.N)r   ri   rT   s    r   ri   ri     s    Mr   c                 4    t               j                  |       S )zGet information about a table.)r   rk   r   s    r   rk   rk     s    =e$$r   c                 6    t               j                  |        y)zEnsure a table exists.N)r   rm   r   s    r   rm   rm     s    Mr   c                 6    t               j                  | |      S )zCheck if a table or key exists.)r   ro   r   s     r   ro   ro     r   r   c                 6    t               j                  |        y)zDelete an entire table.N)r   rq   r   s    r   rq   rq     s    Mu%r   c                  2    t               j                         S )zList all tables.)r   rt   r   r   r   rt   rt     s    =!!r   c                 4    t               j                  |       S )z%Get a dict-like interface to a table.)r   rT   )rw   s    r   rT   rT     s    =t$$r   rz   )(r   r/   r4   errorsr   protocolr   r   r   r   r	   r
   r   r   r   r   r   r   r   r   r&   r*   rv   localr   r   r   r   r   r\   rO   rb   rf   ri   rk   rm   ro   rq   rt   rT   r   r   r   <module>r      s   
*X       / /Aj ABJ Bq qhF4 F4\  	!  )
2
,
.

%
 
,
&
"
%r   