o
    Lj?                     @   sT  d Z ddlZddlmZmZ ddlmZ ddlmZ ddl	m
Z
mZmZ dZdZd	Zd
ZdZe de Ze de Ze de Ze de Zdedee defddZdededee
 defddZdedefddZeeZe ZdefddZ e Z!e Z"dede#defddZ$dd Z%ed d!G d"d# d#Z&G d$d% d%Z'dS )&ad  HIMPORT client-side fieldset registry for redis-py.

`HIMPORT` lets a client register an ordered list of hash field names once per
connection (a *fieldset*) and then create many hashes by sending only values.
Because a fieldset is server-side session state bound to a single physical
connection, redis-py keeps a client-level registry of the fieldsets the
application has declared and prepares them lazily, per connection, on first use
by ``himport_set``.

This module holds only that registry. It is pure in-memory state with no I/O and
no ``asyncio`` primitives, so a single class is shared by both the sync and async
clients.

Example::

    >>> from redis.himport import HImportRegistry
    >>> registry = HImportRegistry()
    >>> registry.prepare("account_data", ["name", "email", "age"])
    >>> registry.get("account_data").fields
    ('name', 'email', 'age')
    N)IterableIterator)	dataclass)	DataError)
EncodableTFieldTKeyTHIMPORTPREPARESETDISCARD
DISCARDALL fieldset_namefieldsreturnc                 C   s   t t| g|R S )zMPositional wire args for ``HIMPORT PREPARE fieldset_name field [field ...]``.)_HIMPORT_PREPARE)r   r    r   K/home/djax/ivt_ai_plugin/venv/lib/python3.10/site-packages/redis/himport.pyhimport_prepare_command5   s   r   keyvaluesc                 C   s   t t| |g|R S )zMPositional wire args for ``HIMPORT SET key fieldset_name value [value ...]``.)r   _SET)r   r   r   r   r   r   himport_set_command:   s   r   c                 C   s
   t t| fS )z;Positional wire args for ``HIMPORT DISCARD fieldset_name``.)r   _DISCARD)r   r   r   r   himport_discard_commandA   s   
r   c                 C   sV   t | trt| tko|  tkS t | tttfr)t| }t|tko(| t	kS dS )ab  Return ``True`` if ``command_name`` names the ``HIMPORT SET`` command.

    Redis command names are case-insensitive on the wire, and a caller using the
    raw ``execute_command`` API may pass the name in any case and as either
    ``str`` or ``bytes`` (e.g. ``execute_command("himport set", ...)`` or
    ``b"HIMPORT SET"``). The connection-state-aware ``HIMPORT SET`` path keys off
    the command name, so it must recognise all of those spellings: an exact
    comparison against :data:`HIMPORT_SET` would miss them and send a bare SET
    that fails with ``no such fieldset`` for a fieldset registered in the client
    but not yet PREPAREd on the borrowed connection. The length check keeps the
    per-command cost on the hot path to a single comparison for the common case
    of a differently-sized command name (``upper()`` runs only on a size match).
    F)

isinstancestrlen_HIMPORT_SET_LENupperHIMPORT_SETbytes	bytearray
memoryview_HIMPORT_SET_BYTES)command_namerawr   r   r   is_himport_set_commandJ   s   

r)   upper_tokenupper_token_bytesc                 C   s^   t |}t| trt | |ko|  |kS t| tttfr-t| }t ||ko,| |kS dS )a  Case-insensitive match of a single ``str``/``bytes`` command token.

    ``upper_token`` / ``upper_token_bytes`` must already be upper-cased. The
    length guard keeps the hot path to a single comparison for a differently
    sized token (``upper()`` runs only on a size match).
    F)r   r   r   r!   r#   r$   r%   )valuer*   r+   nr(   r   r   r   	_token_isg   s   
r.   c                 C   s   | sdS | d }t |r#t| dk rdS | d | d t| dd fS t| dkrNt|ttrNt| d ttrNt| dk r?dS | d | d t| dd fS dS )a  Detect an ``HIMPORT SET`` command in ``args`` and return its operands.

    ``HIMPORT SET`` reaches the raw ``execute_command`` API in two wire-equivalent
    forms that the serializer both accept:

    * the joined form ``("HIMPORT SET", key, fieldset, *values)`` (``args[0]`` is
      the two-word command name the request packer splits on the space), and
    * the split form ``("HIMPORT", "SET", key, fieldset, *values)``.

    Both are case- and encoding-insensitive. The connection-state-aware executor
    and the pipeline pre-flight need the operands at the right offsets for either
    form, so this returns ``(key, fieldset_name, values_list)`` when ``args`` is an
    ``HIMPORT SET`` with enough operands, or ``None`` otherwise (a non-``HIMPORT
    SET`` command, or one with too few operands -- which falls through to the plain
    send so the server returns its own arity error).
    Nr               )r)   r   listr.   r   _HIMPORT_BYTESr   
_SET_BYTES)argsfirstr   r   r   parse_himport_set_argsw   s    
r8   T)frozenc                   @   s2   e Zd ZU dZeed< eedf ed< eed< dS )HImportFieldseta$  An immutable HIMPORT fieldset entry.

    Attributes:
        name: Fieldset name used by ``HIMPORT SET`` / ``HIMPORT DISCARD``.
        fields: Ordered field names, exactly as supplied by the caller. They are
            never reordered or deduplicated (HLD R.2): the server canonicalizes
            field order internally and rejects duplicate field names, so the
            client only preserves the caller's positional order.
        version: Monotonic stamp bumped each time the fieldset is (re)declared.
            Connections compare the version they last prepared against this value
            to detect a stale prepared state; the stamp is drawn from the registry's
            mutation clock (:attr:`HImportRegistry.revision`), which never repeats,
            so discarding and re-declaring the same name yields a fresh version
            rather than a colliding one. This is the prepare-side signal; the
            discard-side counterpart is the clock advancing on removal, since a
            removed fieldset leaves no entry to stamp.
    name.r   versionN)	__name__
__module____qualname____doc__r   __annotations__tupler   intr   r   r   r   r:      s
   
 r:   c                   @   s@  e Zd ZdZd(ddZdefddZedee	 de
fd	d
Zdede
defddZdedee	 defddZdedefddZdefddZedefddZdee dee fddZdededB fddZdee fddZdee
eef  fddZdedefd d!Zdefd"d#Zdee fd$d%Zdefd&d'ZdS ))HImportRegistrya  Client-level registry of HIMPORT fieldsets.

    Pure in-memory state, shared by the sync and async clients. It is mutated only
    through the client's ``himport_prepare`` / ``himport_discard`` /
    ``himport_discard_all`` methods and exposed read-only through the client's
    ``himport_registry`` property. Mutations are serialized under a lock so the
    revision bump and dict change stay consistent when a sync ``Redis`` instance is
    shared across threads. Reads that iterate or snapshot the registry take the same
    lock, so a concurrent mutation cannot make them observe a torn view or raise
    ``dictionary changed size during iteration``; single-key/scalar reads (``get``,
    ``__contains__``, ``__len__``, :attr:`revision`) are atomic and stay lock-free.
    The async client runs single-threaded and never contends.

    The registry always starts empty; fieldsets are declared at runtime through the
    client's ``himport_prepare`` method.
    r   Nc                 C   s   i | _ t | _d| _d S )Nr   )
_fieldsets	threadingLock_lock	_revisionselfr   r   r   __init__   s   

zHImportRegistry.__init__c                 C   s   |  j d7  _ | j S )Nr0   rI   rJ   r   r   r   _advance   s   zHImportRegistry._advancer   c                 C   s2   t | ttttfrtdt| }|std|S )a  Validate and materialize the caller's field iterable into a tuple.

        Done *before* the mutation lock is taken: consuming an arbitrary iterable
        can be slow, or -- for a generator that inspects this same registry -- can
        re-enter a locked read (e.g. ``yield`` then ``registry.names()``). Running
        it under the non-reentrant ``_lock`` would stall every registry user or
        deadlock permanently. Field order is preserved; nothing is reordered or
        deduplicated.
        zWHIMPORT fields must be a collection of field names, not a single string or binary valuez-HIMPORT fieldset must have at least one field)r   r   r#   r$   r%   r   rB   )r   field_tupler   r   r   _materialize_fields   s   z#HImportRegistry._materialize_fieldsr;   rO   c                 C   s    t |||  d}|| j|< |S )N)r;   r   r<   )r:   rN   rE   )rK   r;   rO   fieldsetr   r   r   _set   s   
zHImportRegistry._setc                 C   s@   |  |}| j | ||W  d   S 1 sw   Y  dS )zAdd or replace a fieldset, bumping its version, and return the entry.

        Re-declaring an existing name replaces its fields and bumps its version.
        Field order is preserved verbatim; nothing is reordered or deduplicated.
        N)rP   rH   rR   )rK   r;   r   rO   r   r   r   prepare
  s   
	
$zHImportRegistry.preparec                 C   sZ   | j   || jvr	 W d   dS | j|= |   	 W d   dS 1 s&w   Y  dS )zRemove a fieldset from the registry.

        Returns ``True`` if a fieldset was removed, ``False`` if ``name`` was not
        registered. Advances :attr:`revision` when a fieldset is actually removed.
        NFT)rH   rE   rN   rK   r;   r   r   r   discard  s   
$zHImportRegistry.discardc                 C   sN   | j  t| j}|r| j  |   |W  d   S 1 s w   Y  dS )zRemove all fieldsets and return the number removed.

        Advances :attr:`revision` when at least one fieldset is removed.
        N)rH   r   rE   clearrN   )rK   countr   r   r   discard_all$  s   

$zHImportRegistry.discard_allc                 C   s   | j S )u  Monotonic mutation clock, advanced on every registry change.

        It is the discard-side counterpart to per-fieldset
        :attr:`HImportFieldset.version`. A connection records the revision it last
        reconciled against; when it differs, a discard (or prepare) has occurred
        since, so the connection recomputes which of its prepared fieldsets are no
        longer registered — see :meth:`names_to_discard` — and discards those when
        it is released back to the pool.
        rM   rJ   r   r   r   revision6  s   zHImportRegistry.revisionprepared_namesc                    s<    j   fdd|D W  d   S 1 sw   Y  dS )a5  Return which of ``prepared_names`` are no longer registered.

        Given the fieldset names a connection has prepared on the server, this is
        the set that must be sent ``HIMPORT DISCARD`` (typically when the
        connection is released), because they have been removed from the registry.
        c                    s   g | ]	}| j vr|qS r   rE   ).0r;   rJ   r   r   
<listcomp>N  s    z4HImportRegistry.names_to_discard.<locals>.<listcomp>N)rH   )rK   rZ   r   rJ   r   names_to_discardC  s   
$z HImportRegistry.names_to_discardc                 C   s   | j |S )z;Return the fieldset registered under ``name``, or ``None``.)rE   getrT   r   r   r   r_   P  s   zHImportRegistry.getc                 C   s4   | j  t| jW  d   S 1 sw   Y  dS )z%Return the registered fieldset names.N)rH   r3   rE   rJ   r   r   r   namesT  s   $zHImportRegistry.namesc                 C   s8   | j  t| j W  d   S 1 sw   Y  dS )z0Return a snapshot of ``(name, fieldset)`` pairs.N)rH   r3   rE   itemsrJ   r   r   r   ra   Y  s   $zHImportRegistry.itemsc                 C   s
   || j v S Nr[   rT   r   r   r   __contains__^     
zHImportRegistry.__contains__c                 C   s
   t | jS rb   )r   rE   rJ   r   r   r   __len__a  rd   zHImportRegistry.__len__c                 C   s8   | j  tt| jW  d    S 1 sw   Y  d S rb   )rH   iterr3   rE   rJ   r   r   r   __iter__d  s   $zHImportRegistry.__iter__c                 C   s\   | j  t| j }W d    n1 sw   Y  ddd |D }| jj d| dS )Nz, c                 s   s(    | ]\}}| d t |j V  qdS )=N)r3   r   )r\   r;   rQ   r   r   r   	<genexpr>n  s    
z+HImportRegistry.__repr__.<locals>.<genexpr>())rH   r3   rE   ra   join	__class__r=   )rK   entriesbodyr   r   r   __repr__k  s   
zHImportRegistry.__repr__)r   N) r=   r>   r?   r@   rL   rC   rN   staticmethodr   r   rB   rP   r   r:   rR   rS   boolrU   rX   propertyrY   r3   r^   r_   r`   ra   objectrc   re   r   rg   rp   r   r   r   r   rD      s(    
rD   )(r@   rF   collections.abcr   r   dataclassesr   redis.exceptionsr   redis.typingr   r   r   r   r   r   r   _DISCARDALLHIMPORT_PREPAREr"   HIMPORT_DISCARDHIMPORT_DISCARDALLr   rB   r   r   r   r   r    encoder&   rr   r)   r4   r5   r#   r.   r8   r:   rD   r   r   r   r   <module>   sH    
%